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ü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