gra_app.cpp 41.6 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
/**
 * \file gra_app.cpp
 *
 * \brief Testapplication zu engage
 *
 * Dieses kleine Prog testet die bestehenden Funktionen von engage und
 * dient dazu neue Funktionen testweise einzubauen, bevor sie endgültig
 * in die lib kommen.
 *
 * \author Georg Steffers <georg@steffers.org> [gs]
 *
 * \date 16.12.2003
 *
 * \version ..2002 [gs]: erste Implementation
 * \version 13.12.2003 [gs]: movable eingefügt mit Funktionen f&uuml;r die
 *                           wichtigsten Bewegungen (muss aber noch eine
 *                           gro&szlig;es cleanup und fixing her) schient aber
 *                           f&uuml;r die momentanen bed&uuml;rfnisse zu
 *                           reichen.
 * \version 16.12.2003 [gs]: <ul><li>
 *                           Beginn der Dokumentation mit Doxygen
 *                           </li><li>
 *                           einige kleinere Fixes an movable
 *                           </li></ul>
 * \version 19.12.2003 [gs]: <ul><li>
 *                           Eine Polygonliste eingebaut und diese sortiert,
 *                           so das die am Weitesten vom Betrachter entfernten
 *                           Polygone zuerst gezeichnet werden.
 *                           Dazu musste auch eine Methode geschaffen werden
 *                           mit der man feststellen konnte zu welchem Polyeder
 *                           welches Polygon geh&ouml;rt. Dies habe ich mit
 *                           der Methode contains in der Klasse container
 *                           eingebaut.
 *                           </li><li>
 *                           Die Klasse movable in eine eigenen Datei verschoben
 *                           geometry/movable.h. Au&szlig;erdem die Klassen
 *                           polygon_movable nach geometry/polygon.h und 
 *                           polyeder_movable nach geometry/polyeder.h
 *                           verschoben.
 *                           </li><li>
 *                           Ein viertes Polygon hinzugefügt, das erst 
 *                           verschoben und dann um die Weltachsen gedreht wird.
 *                           Nur um zu demonstrieren was diese Reihenfolge
 *                           der transformation f&uuml;r eine Wirkung hat.
 *                           </li><li>
 *                           kleinere &Auml;nderungen im Darstellungsteil.
 *                           </li><li>
 *                           &uuml;ber die in canvas neu geschaffene Methode
 *                           draw_text die Zeit ss:ms die f&uuml;r einen
 *                           kompletten Transformations/Zeichenvorgang 
 *                           ben&ouml;tigt wird angezeigt.
 *                           </li></ul>
 * \version 21.12.2003 [gs]: <ul><li>
 *                           Herausgefunden warum die Würfel so gross sind
 *                           obwohl sie nur eine Kantenl&auml;nge von 10 haben.
 *                           Es ligt daran das sie mathematisch vor dem 
 *                           screen liegen, dadurch werden sie bei der
 *                           projection gr&ouml;&szlig;er skaliert.
 *                           </li><li>
 *                           Die Klasse Camera eingef&uuml;gt und das 
 *                           Hauptprogramm so umgeschrieben das es eine
 *                           Camera-Instanz nutzt um
 *                           <ol><li>
 *                           eine Kameratransformation zu machen
 *                           </li><li>
 *                           die 2D-Projektion durchzuf&uuml;hren.
 *                           </li></ol>
 *                           </li><li>
 *                           so modifiziert das erst nur die Vertices von
 *                           movable und die Normalenvektoren der Polygone
 *                           berechnet werden, nur wenn das Polygon auch
 *                           wirklich gezeichnet wird werden die weiteren
 *                           Vertices transformiert und projeziert.
 *                           </li><li>
 *                           So umgebaut das das vordere z-clipping der
 *                           Polygone genutzt wird.
 *                           </li></ul>
 * \version 22.12.2003 [gs]: Beleuchtung &uuml;ber die in canvas_imp neu
 *                           eingebauten light_table eingebaut.
 */

/**
 * \mainpage Referenzmanual zu ENGAGE
 *
 * \author Georg Steffers <georg@steffers.org> [gs]
 * \date 22.12.2003
 * \version 0.0.0: Erster Aufbau, jede Menge immer noch zu tun.
 *
 * Dies Projekt war zu anfang eine kleine app um ein wenig x-lib zu
 * Programmieren. Als das ein wenig langweilig wurde und zumindest
 * rudiment&auml;r funktionierte habe ich angefangen eine kleine 3D-Engine
 * dazuzuschreiben.<br>
 * Heute habe ich das Ziel dies ganze in irgendeinem Spiel zu verwenden.
 * Bis dahin ist allerdings noch jede Menge zu tun.
 * Was bislang fertig ist:
 * <ul>
 * <li>Klassen um die X-Lib auf einfacher Ebene anzusprechen, allerdings
 * noch komplett ohne kommunikation mit dem Windowmanager.
 * </li><li>
 * Klasse um Manipulationen und Berechnungen verschiedenster Art an
 * Matrizen und Vektoren mit 4 Tupeln zu machen, wie gauss, verschiedene
 * Wege die Determinante zu berechnen, Inverse &uuml;ber Adjunkte oder
 * &uuml;ber Gauss, Transponieren, Skalarprodukt, Matritzenprodunkt, Addition
 * Subtraktion, etc.
 * </li><li>
 * Das Muster eines Event-Dispatchers sowie ein template von dem dich
 * Klassen ableiten m&uuml;ssen, die Events f&uuml;r diesen erzeugen.
 * </li><li>
 * einen abstrakten canvas der events erzeugt, M&ouml;glihkeiten zur 
 * Farbverwaltung bietet und einen passenden rasterer erzeugt. Davon
 * abgeleitet ein canvas f&uuml;r X11 und wiederum davon abgeleitete ein
 * canvas f&uuml;r X11-SHM.
 * rasterizer ist wiederum eine abstrakte Klasse die eine Schnittstelle bietet
 * um auf einen canvas zu schreiben. davon abgeleitet existiert eine Klasse
 * rasterizer_rgb, der speziell auf ein RGB Canvas schreibt, wie das z.B.
 * in einem Truecolor Grafikmodus unter X11 n&ouml;tig ist.
 * </li><li>
 * Eine Helferklasse container, die verschiedene Daten speichen und
 * organisieren kann. Diese sollte in zukunft abstrakt werden und
 * Implementierungen versiedener Datenstrukturierungsmethoden verdecken,
 * wie z.B. diverse Listenformen, B&auml;me oder einfache Arrays.
 * Im Moment organisiert sie die Daten in einem Array. Einige wichtige Klassen
 * (eigentlich alle die mehrere Elemente nicht bekannter Anzahl verwalten 
 * m&uuml;ssen) leiten sich von dieser Klasse ab. Das ist insbesondere
 * vertex_list, polygon und polyeder.
 * </li><li>
 * Die grundlegenden Geometrieklassen (unterliegen nach wie vor starken
 * Ver&auml;nderungen. Das sind im einzelnen:
 * <ol><li>
 * <b>vertex:</b> Diese Struktur nimmt einen 4-Tupel Vektor auf, der
 * entweder eine 3D-Coordinate oder aber einen Vektor im 3D-Raum beschreibt.
 * es werden sowohl die Originaldaten sowie transformierte Daten gespeichert.
 * Au&szlig;erdem wird der transformationsstatus zwischengespeicher. (Das
 * verhindert das, wenn sich z.B. ein Polygon einen Vertex mit einem anderen
 * teilt, transformationen nur einmal an dem Vertex vorgenommen werden.
 * </li><li>
 * <b>vertex_list:</b> Eine Struktur um mehrere Vertexe zu verwalten.
 * Sie besteht aus einem fixen und einem Variablen Teil, daher modifiziert
 * sie einige Operatoren, die sie von container erbt. Die zweigeteiltheit
 * ist wichtig, da bei einigen Operationen (insb. Clipping) zus&auml;tzliche
 * Vertexe entstehen k&ouml;nnen, die aber nur tempor&auml;r wichtig sind.
 * Durch die Zweigeteiltheit l&auml;&szlig;t sich immer genau unterscheiden
 * was erzeugte und was originale Vertexe waren. Weiterhin ist wichtig,
 * das auf beide Teile mit derselben Zugriffsfunktion zugegriffen werden kann.
 * </li><li>
 * <b>polygon:</b> ist eine Liste von Indexen in eine Vertexliste, die
 * ein Polygon beschreibt. Leitet sich von container ab und verwaltet ihren
 * container teil die indexe. Weiterhin enth&auml;lt polygon einen pointer auf
 * eine vertex_list, auf die sich die Indexwerte bezieht und die Informationen
 * zu einem Normalenvektor (als Basispunkt und Spitze Paar).
 * Seit neustem enth&auml;lt ein Polygon auch noch einen pointer auf den
 * polyeder zu dem es geh&ouml;rt (wenn es zu einem geh&ouml;rt, ansonsten null)
 * und es sollte auch noch den index in diesem Polyeder enth&auml;ten.
 * Damit w&auml;re es dann einfach von einem polygon wieder auf seinen
 * Polyeder zu schlie&szlig;en (es kann aber auch sein das ich dieses Konzept
 * wieder verwerfe und versuche irgendwie &uuml;ber den container eine 
 * M&ouml;glichkeit der zuordnung zu schaffen.<br>
 * Da dies eine polygonale Engine ist enth&auml;lt polygon einige wichtige
 * Methoden, wie das clipping zur vorderen z-ebene und das clipping ein einem
 * weiteren polygon (nach der 2D-Projektion, um das Polygon gegen die
 * Canvas (Rasterer)-Grenzen zu clippen.
 * </li><li>
 * <b>polyeder:</b> ist eine von container abgeleitete Klasse die eine
 * Gruppe zusammengeh&ouml;riger polygone verwaltet. Zusammengeh&ouml;rend
 * bedeutet in diesem Fall vor allem, das sie die gleiche Vertexliste nutzen.
 * </li></ol></li></ul>
 *
 * Was die Engine bislang (mehr oder weniger sauber implementiert) macht:
 * <ul><li>
 * beliebige transformtion beliebiger polygone im 3D-Raum.
 * </li><li>
 * Perspektivische Projektion
 * </li><li>
 * clipping der projezierten Vertexe gegen die canvas-Grenzen
 * </li><li>
 * clipping der 3D-Coordinaten gegen eine vordere Z-Ebene
 * </li><li>
 * falls gew&uuml;nscht Backface-Culling
 * </li><li>
 * definieren einer Kamera und positionieren dieser.
 * </li><li>
 * Darstellung flatshaded, wireframe oder kombiniert.
 * </li><li>
 * Sortiert die polygone nach z, damit die hintersten zuerst gezeichnet werden.
 * </li></ul>
 *
 * \bug [gs] Mein Backfaceculling funktioniert nicht, das scheint daran zu 
 *      liegen, das eine projezierte Seite die 90 Grad zu mir aber stark am 
 *      Rand des sichtbaren Bereichs ist sehr wohl von mir gesehen werden kann.
 *      In die berechnung des cullings muss also irgendwie das Scihtfeld 
 *      bzw der Blickwinkel mit einbezogen werden.
 *      -- Gefixed: man muss als Blickwinkel nur den vrp nehmen, dann klappts.
 * \bug [gs] Das Programm darf nicht mehr mit -ffast-math compiliert werden,
 *      da dann anscheinend irgendwas bei der berechnung der inversen
 *      Camera-Matrix kaputtoptimiert wird.
 *
 * \todo [gs] Evtl. sollte ich f&uuml;r den Rasterizer imlib2 oder evas
 *       verwenden (bzw ich baue einen rasterizer der diese Libs nutzt). 
 *       Evtl. kann evas auch einen kompletten canvas für mich bilden, incl.
 *       Rasterizer. (Auch ein GL Rasterizer sollte noch geschrieben werden.
 * \todo [gs] Eine menge cleanups.
 * \todo [gs] Java Version schreiben und hoffen das sie schnell genug ist.
 * \todo [gs] Nat&uuml;rlich noch jede Menge, Lightmapping, Texturemapping,
 *       Schadowmapping, vorne und hinten z-clipping usw.
 * \todo [gs] gutes Konzept finden um z-clipping, transformieren und
 *       projezieren zu machen (es darf z.B. nich projeziert werden, solange
 *       das z-clipping l&auml;uft.
 * \todo [gs] ein gutes konzept finden ein polygon einem polyeder zuzuweisen,
 *       und den index innerhalb des Polyeders zu bestimmen, auch wenn
 *       das polygon bereits transformiert wurde...in diesem zusammenhang
 *       glaube ich immer mehr, das es doch besser währe, das polygon
 *       wuerde beim clipping doch nicht einfach ein neues Polygon erzeugen
 *       und stattdessen sich selbst modifizieren und eine referenz auf
 *       sich zur&uuml;ckgeben. (Bei den == Operatoren würde ich dann immer
 *       checken ob es sich wirklich um eine Referenz auf die selbe Instanz
 *       (sprich Adressen gleich) ist.
 *
 * Copyright &copy;2003 Georg Steffers
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * <br><br><br>
 */

#ifndef __XProto_h__
#define __XProto_h__

using namespace std;

#include <cmath>
#include <time.h>
#include <unistd.h>

#include "factory.h"
#include "dispatcher.h"
#include "canvas.h"
#include "rasterize.h"
#include "math/Mmn.h"
#include "math/V_K4.h"
#include "math/sin_cos.h"
#include "geometry/vertex.h"
#include "geometry/polygon.h"
#include "geometry/polyeder.h"

#include <time.h>
#include <sys/timeb.h>

int compf(const void* a, const void* b) {
   polygon* _a=(polygon*)a;
   polygon* _b=(polygon*)b;

   return (int)(_a->center_p()[Z] - _b->center_p()[Z])*-1;
}

class light {
   protected:
      unsigned intensity;
      double dist_falloff;

   public:
      light(unsigned intense=255) : intensity(intense) {}

      virtual unsigned get_intensity(void) { return intensity; }
      virtual void set_intensity(unsigned in) { intensity=in; }
};

class ambient_light : public light {
   public:
      ambient_light(unsigned intense=255) : light(intense) {}
};

// Diese Lampe ist &auml;hnlich wie die Sonne, sie beleuchtet die
// gesamte Scenen aus einer Richtung. Diese definiere ich einfach
// als fvw des movable.
class direct_light : public light, public movable {
   public:
      direct_light(unsigned intense=255) : light(intense), movable() {}

      const vertex& get_direction(void) {
         return vfw;
      }
};

class camera : public movable {
   private:
        double lcxa; //!< (lens coverage(x) angle = Sichtfeld(x)-Winkle
        double lcx;    //!< (lens coverage(x)) = Sichtfeld(x)
        double sw;     //!< (screen width) = Breite des Projektionsfensters
        double sh;     //!< (screen height) = Hoehe des Projektionsfensters
        double ph_ar;  /**< \brief (physical aspect ratio) = Seitenverhaeltnis 
                        *          des Anzeigegeraets 
                        */
        double sy;     //!< (scale y) = Wert um y proportional zu projezieren.
        double dz;     //!< (delta z) = Abstand des Auges zum screen

        Mmn<double> cam_trans;

   public:
        camera(double lcxa=60) : movable() {
            this->set_lens_coverage(lcxa);
        }

        camera(const rasterizer& scr, double lcxa=60) : movable() {
            this->set_lens_coverage(lcxa);
            this->sw=scr.get_x_size();
            this->sh=scr.get_y_size();
            this->ph_ar=this->sw/this->sh;
            this->sy=this->sh/this->sw*this->ph_ar;
            this->dz=(this->sh/2)/tan(RAD(this->lcxa/2));
        }

        camera(const rasterizer& scr, double ph_ar, 
               double lcxa) : movable() {
            this->set_lens_coverage(lcxa);
            this->sw=scr.get_x_size();
            this->sh=scr.get_y_size();
            this->ph_ar=ph_ar;
            this->sy=this->sh/this->sw*this->ph_ar;
            this->dz=(this->sh/2)/tan(RAD(this->lcxa/2));
        }

        camera(double sw, double sh, double lcxa=60) : movable() {
            this->set_lens_coverage(lcxa);
            this->sw=sw;
            this->sh=sh;
            this->ph_ar=this->sw/this->sh;
            this->sy=this->sh/this->sw*this->ph_ar;
            this->dz=(this->sh/2)/tan(RAD(this->lcxa/2));
        }

        camera(double sw, double sh, 
               double ph_ar, double lcxa=60) : movable() {
            this->set_lens_coverage(lcxa);
            this->sw=sw;
            this->sh=sh;
            this->ph_ar=ph_ar;
            this->sy=this->sh/this->sw*this->ph_ar;
            this->dz=(this->sh/2)/tan(RAD(this->lcxa/2));
        }

        /** 
         * \brief Setze Sichtfeld. (zwischen 10 und 170)
         *
         * lcx ist eine Konstante die die gr&ouml;&szlig;e der Sichtfeldes
         * beschreibt. Diese Funktion ermittelt diese Konstante anhand von
         * Werten zwischen 10 und 170 wobei das volle menschliche Sichtfeld
         * einem Wert von 100 entspricht. 10 ist extrem Tele und 170 extrem
         * Weitwinkel.
         *
         * \param angle Winkel f&uuml;rs Sichtfeld (zwischen 10 und 170)
         *
         * \pre 10 < angel < 170
         */
        void set_lens_coverage(double angle) {
            lcxa=angle;

            if(lcxa>170) lcxa=170;
            if(lcxa<10) lcxa=10;

            lcx=0.5*(1/tan(lcxa*M_PI/(180.0*2.0)));
        }

        const Mmn<double>& cam_trans_mat(void) {
           cam_trans=t_mat.inv_Ad();

           if(cam_trans.is_null())
              cam_trans=t_mat.gauss(INVERSE);

           return cam_trans;
        }

        double get_lcx(void) const {
           return lcx;
        }

        double get_sw(void) const {
           return sw;
        }

        double get_sh(void) const {
           return sh;
        }

        double get_ph_ar(void) const {
           return ph_ar;
        }

        double get_sy(void) const {
           return sy;
        }

        double get_dz(void) const {
           return dz;
        }
};

/**
 * \brief Das eigentliche Programm
 *
 * Hier werden alle Teile zu einem kleinen Testprogramm zusammengef&uuml;gt.
 */
class app {
    private:
        canvas* scr;          //!< Der Canvas auf dem gezeichnet wird
        rasterizer* ras;      //!< Der Rasterizer zu dem \ref scr
        dispatcher* d;        //!< Der X11-Dispatcher
        disp_manager manager; //!< Der Dispatcher-Manager

        polyeder_movable p;           //!< erster Polyeder
        polyeder_movable p2;          //!< zweiter Polyeder
        polyeder_movable p3;          //!< dritter Polyeder
        polyeder_movable p4;          //!< und weils so lustig ist noch einer

        camera cam;
        direct_light li;
        direct_light li2;
        ambient_light li3;

        double winkel;
        int stepping;
        double cam_winkel;
        int cam_stepping;
        double li_winkel;
        int li_stepping;
        double li2_winkel;
        int li2_stepping;

        double clip_front;

        unsigned secs, secs_tmp;
        unsigned short ms;

        /**
         * \brief Bringt den Canvas auf den Bildschirm
         *
         * Dies ist eine Callback-Methode die zu jedem expose-event u.&auml;.
         * aufgerufen wird um den Inhalt des Canvas auf das Fenster zu
         * &uuml;bertragen. Diese Methode wird ausschlie&szlig;lich vom
         * Dispatcher \ref d aufgerufen.
         *
         * \param es Die Quelle des Events, in diesem Fall also der canvas
         * \param key Wird nicht benutzt
         *
         * \pre redraw mu&szlig; als callback in \ref d registriert sein.
         * \pre \ref scr mu&szlig; ein g&uuml;ltiger canvas sein.
         */
        void redraw(event_source* es, void* key) {
            char buffer[10]="\0";

            sprintf(buffer, "%02d:%03d", secs, ms);

            scr->blit_screen();
            scr->draw_text(5,15,buffer,strlen(buffer));
            //usleep(10);
        }

        /**
         * \brief verarbeitet Tastendr&uuml;cke im Canvas
         *
         * Dies ist eine Callback-Methode die zu jedem keypress-event
         * im Canvas aufgerufen wird. Hier&uuml;ber ist es m&ouml;glich
         * das Programm zu steuern. Diese Methode wird ausschlie&szlig;lich
         * vom Dispatcher \ref d aufgerufen.
         *
         * \param es Die Quelle des Events, in diesem Fall also der Canvas
         * \param key Der Ascii-Code der gedr&uuml;ckten Taste.
         *
         * \pre exitOnEsc mu&szlig; als callback in \ref d registriert sein.
         * \pre \ref scr mu&szlig; ein g&uuml;ltiger canvas sein.
         *
         * \post Falls ESC gedr&uuml;ckt wurde -> Programmende
         * \post Falls s oder S gedr&uuml;ckt wurde Dispatcher anhalten
         * \post Falls c oder C gedr%uuml;ckt wurde Dispatcher starten
         */
        void exitOnEsc(event_source* es, void* key) {
            switch (*(char*)key) {
                case 27:
                    d->trigger_event(Stop);
                case 'x':
                case 'X':
                    d->halt();
                    break;
                case 'c':
                case 'C':
                    d->cont();
                    break;
                case '+':
                    cam_stepping=(cam_stepping+1)%11;
                    break;
                case '-':
                    cam_stepping=cam_stepping>0?(cam_stepping-1)%11:10;
                    break;
                case 'q':
                case 'Q':
                    clip_front=clip_front>0?clip_front-10:0;
                    break;
                case 'w':
                case 'W':
                    clip_front=clip_front<100?clip_front+10:100;
                    break;
                case 'r':
                case 'R':
                    li_stepping=(li_stepping+1)%11;
                    break;
                case 'e':
                case 'E':
                    li_stepping=li_stepping>0?(li_stepping-1)%11:10;
                    break;
                case 'z':
                case 'Z':
                    li2_stepping=(li2_stepping+1)%11;
                    break;
                case 't':
                case 'T':
                    li2_stepping=li2_stepping>0?(li2_stepping-1)%11:10;
                    break;
                case 'd':
                case 'D':
                    stepping=(stepping+1)%11;
                    break;
                case 's':
                case 'S':
                    stepping=0;
                    break;
                case 'a':
                case 'A':
                    stepping=stepping>0?(stepping-1)%11:10;
                default:
                    break;
            }
        }

        /**
         * \brief Rotiert, projeziert und zeichnet die drei Polyeder 
         *        \ref p, \ref p2 und \ref p3
         *
         * Hier werden mit Hilfe der Klassen aus \ref Mmn.h, \ref sin_cos.h und 
         * \ref V_K4.h die Polyeder gedreht, skaliert, projeziert und 
         * schlie&szlig;lich mit Hilfe der Methoden des Rasterizers \ref ras
         * auch noch gezeichnet. Am ende wird dann ein Expose-Event 
         * ausgel&ouml;st, damit die &Auml;nderungen auch am Bildschirm
         * sichtbar werden.
         *
         * \note Im Moment wir rotate auch über disp_x11 aufgerufen, das ist 
         * nicht notwendig, und eigentlich sogar falsch, denn es wird überhaupt
         * nicht auf ein X-Event reagiert. Was ich brauche ist ein weiteren 
         * Dispatcher (disp_app) der Application-Events aufführt,
         * Die Rotation müsste dann quasi im idle-Fall ausgeführt werden.
         * Es würde sicherlich Sinn machen diesen dispatcher dann irgendwie
         * als thread zu implementieren. Immerhin ist X ja auch ein eigener
         * Prozess. (OK, der Vergleich hinkt, da ich die XEvents auch im
         * Hauptprozess abarbeite.
         *
         * \param es Die Quelle des Events, in diesem Fall also der canvas
         * \param dummy Wird nicht benutzt
         *
         * \pre redraw mu&szlig; als callback in \ref d registriert sein.
         * \pre \ref scr mu&szlig; ein g&uuml;ltiger canvas sein.
         *
         * \post Die Polyeder sind transformiert und auf dem Canvas
         *       gezeichnet. Ein Expose-Event wurde ausgel&ouml;st.
         */
        void rotate(event_source* es, void* dummy) {
            struct timeb tb1;
            struct timeb tb2;

            ftime(&tb1);
            
            winkel=WINKEL(winkel+stepping);

            cam_winkel=WINKEL(cam_winkel+cam_stepping);
            li_winkel=WINKEL(li_winkel-li_stepping);
            li2_winkel=WINKEL(li2_winkel+li2_stepping);

            container<polygon> po_list, po_list_zclipped;

            //if(winkel==3)
            //   sleep(10);

            p.rotate_im_g_x(45);
            p.translate_im_global(-30.0*sin(RAD(winkel)), 
                                  5.0*cos(RAD(winkel)),
                                  10.0*cos(RAD(winkel)));
            p.rotate_im_vfw(WINKEL(2*winkel));

            p2.translate_im_global(5.0*cos(RAD(winkel)), 
                                   20.0*sin(RAD(winkel)),
                                   20.0*cos(RAD(winkel)));
            p2.rotate_im_l_z(WINKEL(winkel));
            p2.rotate_im_l_y(WINKEL(360-winkel));
            p2.translate_im_global(0,0,-5);

            //p3.translate_im_global(0,0,5);
            
            p4.translate_im_global(0,0,15);
            p4.rotate_im_g_x(WINKEL(winkel));
            p4.rotate_im_g_y(WINKEL(winkel));
            p4.translate_im_global(0,0,5);

            cam.translate_im_global(0,0,-60);
            cam.rotate_im_g_y(WINKEL(-cam_winkel));
            cam.rotate_im_axis(WINKEL(cam_winkel), 
                               vertex(1,0,1,COORD), vertex(0,0,0,COORD));

            li.translate_im_global(0,0,-10);
            li.rotate_im_g_y(WINKEL(45.0));
            li.rotate_im_axis(WINKEL(li_winkel), 
                              vertex(1,0,-1,COORD), vertex(0,0,0,COORD));

            li2.translate_im_global(0,0,-10);
            li2.rotate_im_g_x(WINKEL(li2_winkel));

            Mmn<double> ctm(cam.cam_trans_mat());

            p.move_im(ctm);
            p2.move_im(ctm);
            p3.move_im(ctm);
            p4.move_im(ctm);
            li.move_im(ctm);
            li2.move_im(ctm);

            /* p.transform(1);
            p2.transform(1);
            p3.transform(1);
            p4.transform(1);

            cam.transform(1); */

            // Zu Anfang reicht es die Vertexe für die Objektposition und
            // die Normalenvertexe zu transformieren, dann kommt das
            // backface culling und nur wenn ein polygon wirklich gezeichnet
            // wird mache ich die letzten transformationen und die
            // projektion.
            p.transform_normals(1);
            p2.transform_normals(1);
            p3.transform_normals(1);
            p4.transform_normals(1);

            p.movable::transform(1);
            p2.movable::transform(1);
            p3.movable::transform(1);
            p4.movable::transform(1);

            li.transform(1);
            li2.transform(1);

            /* p.project_2d(cam.get_lcx(), cam.get_sw(), cam.get_sh(), 
                         cam.get_ph_ar(), cam.get_sy(), 2);
            p2.project_2d(cam.get_lcx(), cam.get_sw(), cam.get_sh(), 
                          cam.get_ph_ar(), cam.get_sy(), 2);
            p3.project_2d(cam.get_lcx(), cam.get_sw(), cam.get_sh(), 
                          cam.get_ph_ar(), cam.get_sy(), 2);
            p4.project_2d(cam.get_lcx(), cam.get_sw(), cam.get_sh(), 
                          cam.get_ph_ar(), cam.get_sy(), 2); */

            // Ich muss nach einem verdammt guten Weg suchen herauszufinden
            // zu welchem polyeder ein polygon gehoert, ich denke das beste ist
            // einen pointer auf den polyeder im polygon zu speichern wenn
            // es einem polyeder zugeordnet wird.
            vertex view, normal, center, norm_v;

            for(unsigned i=0; i<p.card(); i++) {
               view=p.get_vrp();

               center=p[i].center_p();
               normal=p[i].normal_p();
               norm_v=normal-center;

               if(((norm_v%view)/(norm_v.betr()*view.betr())) < 0.0) {
                  po_list[po_list.card()]=p[i];
                  po_list[po_list.card()-1].transform(p.get_t_mat(), 1);
               }
            }

            for(unsigned i=0; i<p2.card(); i++) {
               view=p2.get_vrp();

               center=p2[i].center_p();
               normal=p2[i].normal_p();
               norm_v=normal-center;

               if(((norm_v%view)/(norm_v.betr()*view.betr())) < 0.0) {
                  po_list[po_list.card()]=p2[i];
                  po_list[po_list.card()-1].transform(p2.get_t_mat(), 1);
               }
            }

            for(unsigned i=0; i<p3.card(); i++) {
               //hier kein backface-culling...

               //view=p3.get_vrp();

               //center=p3[i].center_p();
               //normal=p3[i].normal_p();
               //norm_v=normal-center;

               //if(((norm_v%view)/(norm_v.betr()*view.betr())) < 0.0) {
                  po_list[po_list.card()]=p3[i];
                  po_list[po_list.card()-1].transform(p3.get_t_mat(), 1);
               //}
            }

            for(unsigned i=0; i<p4.card(); i++) {
               view=p4.get_vrp();

               center=p4[i].center_p();
               normal=p4[i].normal_p();
               norm_v=normal-center;

               if(((norm_v%view)/(norm_v.betr()*view.betr())) < 0.0) {
                  po_list[po_list.card()]=p4[i];
                  po_list[po_list.card()-1].transform(p4.get_t_mat(), 1);
               }
            }

            po_list.sort(compf);

            for(unsigned i=0; i<po_list.card(); i++) {
               po_list_zclipped[i]=po_list[i].clip_front_z(clip_front);
            }

            ras->clear_buffer();

            double normal_X[6];
            double normal_Y[6];

            for(unsigned i=0; i<po_list.card(); i++) {
               int idx;

               if((idx=p3.contains(po_list[i])) >= 0) {
                  unsigned long col;

                  vertex center=po_list[i].center_p();
                  vertex normal=po_list[i].normal_p();
                  vertex norm_v=normal-center;


                  if(idx==1)
                     col=scr->get_color(0,0,255);
                  else
                     col=scr->get_color(0,255,255);

                  vertex light(li.get_direction());

                  unsigned in=0, in_tmp;
                  double intense=(norm_v.norm()%light.norm())*-1;
                  intense=intense<0.02?0.02:intense;
                  intense=intense>1.0?1.0:intense;

                  in_tmp=(unsigned)(intense*li.get_intensity());
                  in=in_tmp>in?in_tmp:in;

                  light=li2.get_direction();

                  intense=(norm_v.norm()%light.norm())*-1;
                  intense=intense<0.02?0.02:intense;
                  intense=intense>1.0?1.0:intense;

                  in_tmp=(unsigned)(intense*li2.get_intensity());
                  in=in_tmp>in?in_tmp:in;
                  in_tmp=(unsigned)(li3.get_intensity());
                  in=in_tmp>in?in_tmp:in;

                  scr->light_native(&col, in);

                  if(po_list_zclipped[i].card() >= 3) {
                     po_list_zclipped[i].project_2d(cam.get_lcx(), cam.get_sw(),
                                                 cam.get_sh(), cam.get_ph_ar(), 
                                                 cam.get_sy(), 2);
                     ras->draw_polygon_flat(po_list_zclipped[i], col);
                  }
               }

               if((idx=p4.contains(po_list[i])) >= 0) {
                  unsigned long col=scr->get_color(0,0,255);

                  vertex center=po_list[i].center_p();
                  vertex normal=po_list[i].normal_p();
                  vertex norm_v=normal-center;

                  vertex light(li.get_direction());

                  unsigned in=0, in_tmp;
                  double intense=(norm_v.norm()%light.norm())*-1;
                  intense=intense<0.02?0.02:intense;
                  intense=intense>1.0?1.0:intense;

                  in_tmp=(unsigned)(intense*li.get_intensity());
                  in=in_tmp>in?in_tmp:in;

                  light=li2.get_direction();

                  intense=(norm_v.norm()%light.norm())*-1;
                  intense=intense<0.02?0.02:intense;
                  intense=intense>1.0?1.0:intense;

                  in_tmp=(unsigned)(intense*li2.get_intensity());
                  in=in_tmp>in?in_tmp:in;
                  in_tmp=(unsigned)(li3.get_intensity());
                  in=in_tmp>in?in_tmp:in;

                  scr->light_native(&col, in);

                  if(po_list_zclipped[i].card() >= 3) {
                     po_list_zclipped[i].project_2d(cam.get_lcx(), cam.get_sw(),
                                               cam.get_sh(), cam.get_ph_ar(), 
                                               cam.get_sy(), 2);
                     ras->draw_polygon_flat(po_list_zclipped[i], col);
                     //ras->draw_polygon_wire(po_list_zclipped[i], 
                     //                    scr->get_color(255, 255, 255));
                  }
               }

               if((idx=p.contains(po_list[i])) >= 0) {
                  unsigned long col=scr->get_color(255,0,0);

                  vertex center=po_list[i].center_p();
                  vertex normal=po_list[i].normal_p();
                  vertex norm_v=normal-center;

                  vertex light(li.get_direction());

                  unsigned in=0, in_tmp;
                  double intense=(norm_v.norm()%light.norm())*-1;
                  intense=intense<0.02?0.02:intense;
                  intense=intense>1.0?1.0:intense;

                  in_tmp=(unsigned)(intense*li.get_intensity());
                  in=in_tmp>in?in_tmp:in;

                  light=li2.get_direction();

                  intense=(norm_v.norm()%light.norm())*-1;
                  intense=intense<0.02?0.02:intense;
                  intense=intense>1.0?1.0:intense;

                  in_tmp=(unsigned)(intense*li2.get_intensity());
                  in=in_tmp>in?in_tmp:in;
                  in_tmp=(unsigned)(li3.get_intensity());
                  in=in_tmp>in?in_tmp:in;

                  scr->light_native(&col, in);

                  center.project_2d(cam.get_lcx(), cam.get_sw(), 
                                    cam.get_sh(), cam.get_ph_ar(), 
                                    cam.get_sy(), 
                                    center.get_trans_stage()+1);
                  normal.project_2d(cam.get_lcx(), cam.get_sw(), 
                                    cam.get_sh(), cam.get_ph_ar(), 
                                    cam.get_sy(), 
                                    center.get_trans_stage()+1);

                  if(po_list_zclipped.card() >= 3) {
                     po_list_zclipped[i].project_2d(cam.get_lcx(), cam.get_sw(),
                                               cam.get_sh(), cam.get_ph_ar(), 
                                               cam.get_sy(), 2);
                     ras->draw_polygon_flat(po_list_zclipped[i], col);
                     //ras->draw_polygon_wire(po_list_zclipped[i], 
                     //                    scr->get_color(255, 255, 255));
                     ras->line(center[X], center[Y], normal[X], normal[Y],
                               scr->get_color(0, 0, 0));
                  }
               }

               if((idx=p2.contains(po_list[i])) >= 0) {
                  unsigned long col=scr->get_color(0,255,0);

                  vertex center=po_list[i].center_p();
                  vertex normal=po_list[i].normal_p();
                  vertex norm_v=normal-center;

                  vertex light(li.get_direction());

                  unsigned in=0, in_tmp;
                  double intense=(norm_v.norm()%light.norm())*-1;
                  intense=intense<0.02?0.02:intense;
                  intense=intense>1.0?1.0:intense;

                  in_tmp=(unsigned)(intense*li.get_intensity());
                  in=in_tmp>in?in_tmp:in;

                  light=li2.get_direction();

                  intense=(norm_v.norm()%light.norm())*-1;
                  intense=intense<0.02?0.02:intense;
                  intense=intense>1.0?1.0:intense;

                  in_tmp=(unsigned)(intense*li2.get_intensity());
                  in=in_tmp>in?in_tmp:in;
                  in_tmp=(unsigned)(li3.get_intensity());
                  in=in_tmp>in?in_tmp:in;

                  scr->light_native(&col, in);

                  center.project_2d(cam.get_lcx(), cam.get_sw(), cam.get_sh(), 
                                    cam.get_ph_ar(), cam.get_sy(), 
                                    center.get_trans_stage()+1);
                  normal.project_2d(cam.get_lcx(), cam.get_sw(), cam.get_sh(), 
                                    cam.get_ph_ar(), cam.get_sy(), 
                                    center.get_trans_stage()+1);

                  if(po_list_zclipped[i].card() >= 3) {
                     po_list_zclipped[i].project_2d(cam.get_lcx(), cam.get_sw(),
                                            cam.get_sh(), cam.get_ph_ar(), 
                                            cam.get_sy(), 2);
                     ras->draw_polygon_flat(po_list_zclipped[i], col);
                     //ras->draw_polygon_wire(po_list_zclipped[i], 
                     //                     scr->get_color(255, 255, 255));
                     ras->line(center[X], center[Y], normal[X], normal[Y],
                               scr->get_color(0, 0, 0));
                  }
               }
            }

            //ras->draw_polyeder_wire(p, scr->get_color(255,255,255));
            //ras->draw_polyeder_wire(p2, scr->get_color(255,255,255));

            p.reset();
            p2.reset();
            p3.reset();
            p4.reset();
            cam.reset();
            li.reset();
            li2.reset();

            ftime(&tb2);

            secs_tmp=tb2.time-tb1.time;
            ms=(secs_tmp*1000+tb2.millitm)-tb1.millitm;
            secs=(secs_tmp>0)?ms/(secs_tmp*1000):0;
            ms=(secs_tmp>0)?ms%(secs_tmp*1000):ms;

            d->trigger_event(Draw);
        }

    public:
        /**
         * \brief Konstruktor
         *
         * Alle Grundlegenden Initialisierungen. Canvas, Dispatcher und
         * Rasterizer erzeugen. Callbacks registrieren und
         * Polyeder erzeugen. Eckwerte f&uuml;r die 2D-Projektion festlegen.
         */
        app() {
            // Der factory_manager erzeugt mir den gew&uuml;nschten 
            // Canvas und Dispatcher.
            factory_manager f;

            // Erzeuge den Canvas, Dispatcher und Rasterizer
            scr=f.canvas_f->create(400, 300);
            d=f.dispatcher_f->create(scr);
            ras=scr->create_rasterizer();

            // Registriere die Callbacks und trage Dispatcher in Manager ein
            d->register_callback(Key, mk_callback(*this, &app::exitOnEsc));
            d->register_callback(Idle, mk_callback(*this, &app::rotate));
            d->register_callback(Draw, mk_callback(*this, &app::redraw));

            manager[0]=d;
            d->register_callback(Stop, 
                                 mk_callback(manager, &disp_manager::stop));

            // Dann wolln wir die cam mal initilisieren.
            cam=camera(*ras, 50.0);
            li2.set_intensity(127);
            li3.set_intensity(40);

            // zuerst werden alle n&ouml;tigen Vertexe erzeugt
            vertex v[]={vertex(5,5,5,1),
                        vertex(5,-5,5,1),
                        vertex(-5,-5,5,1),
                        vertex(-5,5,5,1),
                        vertex(5,5,-5,1),
                        vertex(5,-5,-5,1),
                        vertex(-5,-5,-5,1),
                        vertex(-5,5,-5,1)};

            // und in einer Vertexliste gespeicher.
            vertex_list vl(v, 8);

            // ein Hilfsarray erzeugt um damit
            unsigned v_idx[][4]={{3,2,1,0},  // hinten
                                 {0,1,5,4},  // rechts
                                 {4,5,6,7},  // vorne
                                 {7,6,2,3},  // links
                                 {4,0,3,7},  // oben
                                 {1,5,6,2}}; // unten

            // die Polygone zu definieren.
            // (Der letzte Parameter ist die Vertexliste die das Polygon
            //  nutzen soll. Hier kann ich NULL nehmen, da sie beim erzeugen
            //  der Polyeders auf dessen Vertexliste gesetzt werden. 
            //  (siehe unten))
            /*polygon_movable pa[]={
               polygon_movable((unsigned*)v_idx[0], 4, NULL),
               polygon_movable((unsigned*)v_idx[1], 4, NULL),
               polygon_movable((unsigned*)v_idx[2], 4, NULL),
               polygon_movable((unsigned*)v_idx[3], 4, NULL),
               polygon_movable((unsigned*)v_idx[4], 4, NULL),
               polygon_movable((unsigned*)v_idx[5], 4, NULL)};*/
            polygon pa[]={
               polygon((unsigned*)v_idx[0], 4, NULL),
               polygon((unsigned*)v_idx[1], 4, NULL),
               polygon((unsigned*)v_idx[2], 4, NULL),
               polygon((unsigned*)v_idx[3], 4, NULL),
               polygon((unsigned*)v_idx[4], 4, NULL),
               polygon((unsigned*)v_idx[5], 4, NULL)};


            // Jetzt definieren wir mit der oben erzeugten Vertexlist und
            // dem Array aus Polygonen 2 Polyeder, die wir beide vom
            // Ursprung auf der Z-Achse weg bewegen.
            p=polyeder(pa, 6, vl);
            p2=polyeder(pa, 6, vl);
            p4=polyeder(pa, 6, vl);
 
            p[2].set_id("DEBUG");
            cout << "BLA: " << p[2].get_id() << "\n";

            polygon pa3[]={polygon((unsigned*)v_idx[0], 4, NULL),
                           polygon((unsigned*)v_idx[2], 4, NULL)};
            p3=polyeder(pa3, 2, vl);

            winkel=0;
            stepping=0;
            cam_winkel=0;
            cam_stepping=0;
            li_winkel=WINKEL(-45.0);
            li_stepping=0;
            li2_winkel=WINKEL(90.0);
            li2_stepping=0;
            clip_front=0;
            //halt=false;
        }

        /**
         * \brief Destruktor
         *
         * freigeben und entfernen alles dynamisch erzeugten Elemente.
         */
        ~app() {
            delete ras;
            delete d;
            delete scr;
        }

        /**
         * \brief startet den Manager und damit das Programm.
         */
        void go(void) {
            manager.start();
        }
};

int main(int argc, char* argv[]) {
    app a;

    a.go();

    // cout << "jokus\n"; 

    return 0;
}

#endif