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
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ür die
|
|
* wichtigsten Bewegungen (muss aber noch eine
|
|
* großes cleanup und fixing her) schient aber
|
|
* für die momentanen bedü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ö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ß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ür eine Wirkung hat.
|
|
* </li><li>
|
|
* kleinere Änderungen im Darstellungsteil.
|
|
* </li><li>
|
|
* über die in canvas neu geschaffene Methode
|
|
* draw_text die Zeit ss:ms die für einen
|
|
* kompletten Transformations/Zeichenvorgang
|
|
* benö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änge von 10 haben.
|
|
* Es ligt daran das sie mathematisch vor dem
|
|
* screen liegen, dadurch werden sie bei der
|
|
* projection größer skaliert.
|
|
* </li><li>
|
|
* Die Klasse Camera eingefü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ü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 ü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ä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 über Adjunkte oder
|
|
* über Gauss, Transponieren, Skalarprodukt, Matritzenprodunkt, Addition
|
|
* Subtraktion, etc.
|
|
* </li><li>
|
|
* Das Muster eines Event-Dispatchers sowie ein template von dem dich
|
|
* Klassen ableiten müssen, die Events für diesen erzeugen.
|
|
* </li><li>
|
|
* einen abstrakten canvas der events erzeugt, Möglihkeiten zur
|
|
* Farbverwaltung bietet und einen passenden rasterer erzeugt. Davon
|
|
* abgeleitet ein canvas für X11 und wiederum davon abgeleitete ein
|
|
* canvas fü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ö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ä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ü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ä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ß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ätzliche
|
|
* Vertexe entstehen können, die aber nur temporär wichtig sind.
|
|
* Durch die Zweigeteiltheit läß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ä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ält ein Polygon auch noch einen pointer auf den
|
|
* polyeder zu dem es gehört (wenn es zu einem gehört, ansonsten null)
|
|
* und es sollte auch noch den index in diesem Polyeder enthäten.
|
|
* Damit wäre es dann einfach von einem polygon wieder auf seinen
|
|
* Polyeder zu schließen (es kann aber auch sein das ich dieses Konzept
|
|
* wieder verwerfe und versuche irgendwie über den container eine
|
|
* Möglichkeit der zuordnung zu schaffen.<br>
|
|
* Da dies eine polygonale Engine ist enthä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öriger polygone verwaltet. Zusammengehö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ü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ü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ü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ä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ü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 ©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 ä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öß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ü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ü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.ä.
|
|
* aufgerufen wird um den Inhalt des Canvas auf das Fenster zu
|
|
* übertragen. Diese Methode wird ausschließ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ß als callback in \ref d registriert sein.
|
|
* \pre \ref scr muß ein gü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ücke im Canvas
|
|
*
|
|
* Dies ist eine Callback-Methode die zu jedem keypress-event
|
|
* im Canvas aufgerufen wird. Hierüber ist es möglich
|
|
* das Programm zu steuern. Diese Methode wird ausschließ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ückten Taste.
|
|
*
|
|
* \pre exitOnEsc muß als callback in \ref d registriert sein.
|
|
* \pre \ref scr muß ein gültiger canvas sein.
|
|
*
|
|
* \post Falls ESC gedrückt wurde -> Programmende
|
|
* \post Falls s oder S gedrü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ßlich mit Hilfe der Methoden des Rasterizers \ref ras
|
|
* auch noch gezeichnet. Am ende wird dann ein Expose-Event
|
|
* ausgelöst, damit die Ä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ß als callback in \ref d registriert sein.
|
|
* \pre \ref scr muß ein gültiger canvas sein.
|
|
*
|
|
* \post Die Polyeder sind transformiert und auf dem Canvas
|
|
* gezeichnet. Ein Expose-Event wurde ausgelö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ür die 2D-Projektion festlegen.
|
|
*/
|
|
app() {
|
|
// Der factory_manager erzeugt mir den gewü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ö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
|