This is old C++ code originally intended to be a playground for 3D math.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1076 lines
42 KiB

/**
* \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