Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - Quentin

Pages: 1 2 [3] 4 5 6
31
GLBasic - de / X_OBJ... und Texturen
« on: 2008-Nov-17 »
Eine Sache mit der Texturierung von Objekten ist mir noch nicht so ganz klar. In jedem Beispiel, sei es hier in GLBasic (Samples) oder in OpenGL-Tutorien, werden als Texturkoordinaten immer nur 0 und 1 verwendet. Wenn ich jedoch Objekte unterschiedlicher Größe habe und belege beide Objekte mit der gleichen Textur, so erscheinen die Texturen auch in unterschiedlicher Auflösung, wenn ich als Koordinaten nur 0 und 1 verwende. Wenn ich jedoch die Texturkoordinaten im Verhältnis umrechne (also 1 = 100%, Dreisatz ;), dann erscheint die Textur auf allen Flächen in der gleichen Auflösung, egal wie groß die Fläche ist.

Folgendes Beispiel soll das verdeutlichen:

zunächst werden zwei Quadrate gezeichnet, eines mit 100 Punkten Kantenlänge, eines mit 50. Als Texturkoordinaten verwende ich bei beiden nur 0 und 1.

die zwei Quadrate darunter haben die gleiche Dimension, die Texturkoordinaten für das kleinere Quadrat wurden jedoch im Verhältnis angeaßt. (die verwendete Textur findet sich im Anhang)

Code: (glbasic) [Select]
LOADSPRITE "mauer.png", 0

c = RGB(255, 255, 255)
X_OBJSTART 0
X_OBJADDVERTEX -100, 100, 0, 0, 0, c
X_OBJADDVERTEX -100,   0, 0, 0, 1, c
X_OBJADDVERTEX    0, 100, 0, 1, 0, c
X_OBJADDVERTEX    0,   0, 0, 1, 1, c
X_OBJEND

X_OBJSTART 1
X_OBJADDVERTEX    0,  50, 0, 0, 0, c
X_OBJADDVERTEX    0,   0, 0, 0, 1, c
X_OBJADDVERTEX   50,  50, 0, 1, 0, c
X_OBJADDVERTEX   50,   0, 0, 1, 1, c
X_OBJEND

X_OBJSTART 2
X_OBJADDVERTEX -100,  -10, 0, 0, 0, c
X_OBJADDVERTEX -100, -110, 0, 0, 1, c
X_OBJADDVERTEX    0,  -10, 0, 1, 0, c
X_OBJADDVERTEX    0, -110, 0, 1, 1, c
X_OBJEND

X_OBJSTART 3
X_OBJADDVERTEX   0,   -10, 0, 0, 0, c
X_OBJADDVERTEX   0,   -60, 0, 0, 0.5, c
X_OBJADDVERTEX  50,   -10, 0, 0.5, 0, c
X_OBJADDVERTEX  50,   -60, 0, 0.5, 0.5, c
X_OBJEND

GLOBAL KEY_LEFT  = 203
GLOBAL KEY_RIGHT = 205
GLOBAL KEY_UP    = 200
GLOBAL KEY_DOWN  = 208
GLOBAL KEY_W     = 17
GLOBAL KEY_S     = 31

GLOBAL camx, camy, camz, camdx, camdy, camdz
GLOBAL angle, factor

camy = 0

camdx = 0
camdy = 0
camdz = 0

angle = 90
distance = 300


WHILE TRUE

IF KEY(KEY_LEFT)
DEC angle, 1
ENDIF
IF KEY(KEY_RIGHT)
INC angle, 1
ENDIF

IF KEY(KEY_W) THEN DEC distance, 3
IF KEY(KEY_S) THEN INC distance, 3

IF KEY(KEY_UP) THEN DEC camy, 5
IF KEY(KEY_DOWN) THEN INC camy, 5

camx = COS(angle) * distance
camz = SIN(angle) * distance

X_MAKE3D 1, 1000, 45
X_CAMERA camx, camy, camz, camdx, camdy, camdz
FOR i = 0 TO 3
X_SETTEXTURE 0, -1
X_DRAWOBJ i, 0
NEXT

SHOWSCREEN

WEND

wie man sieht, hat im unteren Beispiel die Textur auf beiden Flächen die gleiche Auflösung. Somit wirken auch unterschiedliche große Stücke der "Mauer" wie aus einem Guß. Im obigen Beispiel dagegen meint man, im rechten Mauerteil wären kleinere Steine verbaut worden.

Meine eigentliche Frage:
Ist das der Weg, wie man das Ziel "gleiche Texturauflösung auf unterschiedlich großen Flächen" erreicht? Oder gibt es da einfachere oder bessere Wege?

32
GLBasic - de / Frage zu X_OBJADDVERTEX
« on: 2008-Nov-14 »
Da wir zur Zeit unser Häuschen umbauen und wir uns das (zwecks Farbgebung) besser vorstellen können wollen, hab ich spaßeshalber mal ein ein 3D-Model des Hauses erstellt und zwar mit XOBJ.. - Befehlen. Das klappt auch schon ganz gut. Allerdings fällt mir auf, daß, wenn ich Objekte, die umittelbar nebeneinander liegen sollen, z.B. Mauer und Fenster, einen unschönen Zwischenraum aufweisen.

Um das zu verdeutlichen, habe ich mal ein kleines Beispiel gemacht, bei dem dieser Effekt auch auftritt. Einfach ein Stück Mauer als Objekt 0 und die entsprechende Tür als Objekt 1. Beide sollten eigentlich randlos ineinander übergehen. Am linken Rand der Tür zeigt sich jedoch eine kleine Lücke, die ich mir beim besten Willen nicht erklären kann.

Mache ich hier prinzipiell etwas falsch? Also altbekannte Frage: Bug oder Feature?

(mit den Cursortasten kann man das Modellchen um den Mittelpunkt drehen. Mit "W" näher heranzoomen, mit "S" wieder weiter weg zoomen)

Code: (glbasic) [Select]
// --------------------------------- //
// Project: Test_XOBJADDVERTEX
// Start: Friday, November 14, 2008
// IDE Version: 5.360

c = RGB(0, 255, 0)
d = RGB(0, 0, 255)


X_OBJSTART 0
X_OBJADDVERTEX -100,    0,    0,    0,  100, c
X_OBJADDVERTEX -100,  100,    0,    0,    0, c
X_OBJADDVERTEX  -50,    0,    0,   50,  100, c
X_OBJADDVERTEX  -50,  100,    0,   50,    0, c
X_OBJNEWGROUP
X_OBJADDVERTEX  -50,   80,    0,    0,   20, c
X_OBJADDVERTEX  -50,  100,    0,    0,    0, c
X_OBJADDVERTEX   50,   80,    0,  100,   20, c
X_OBJADDVERTEX   50,  100,    0,  100,    0, c
X_OBJNEWGROUP
X_OBJADDVERTEX   50,    0,    0,    0,  100, c
X_OBJADDVERTEX   50,  100,    0,    0,    0, c
X_OBJADDVERTEX  100,    0,    0,   50,  100, c
X_OBJADDVERTEX  100,  100,    0,   50,    0, c
X_OBJEND

X_OBJSTART 1
X_OBJADDVERTEX  -50,    0,    0,    0,   80, d
X_OBJADDVERTEX  -50,   80,    0,    0,    0, d
X_OBJADDVERTEX   50,    0,    0,  100,   80, d
X_OBJADDVERTEX   50,   80,    0,  100,    0, d
X_OBJEND


GLOBAL KEY_LEFT  = 203
GLOBAL KEY_RIGHT = 205
GLOBAL KEY_UP    = 200
GLOBAL KEY_DOWN  = 208
GLOBAL KEY_W     = 17
GLOBAL KEY_S     = 31

GLOBAL camx, camy, camz, camdx, camdy, camdz
GLOBAL angle, factor

camy = 50

camdx = 0
camdy = 50
camdz = 0

angle = 90
distance = 200


WHILE TRUE

IF KEY(KEY_LEFT)
DEC angle, 1
ENDIF
IF KEY(KEY_RIGHT)
INC angle, 1
ENDIF

IF KEY(KEY_W) THEN DEC distance, 3
IF KEY(KEY_S) THEN INC distance, 3

IF KEY(KEY_UP) THEN DEC camy, 5
IF KEY(KEY_DOWN) THEN INC camy, 5

camx = COS(angle) * distance
camz = SIN(angle) * distance

X_MAKE3D 1, 1000, 45
X_CAMERA camx, camy, camz, camdx, camdy, camdz
X_AMBIENT_LT 0, RGB(255, 255, 255)
X_DRAWAXES 0, 0, 0
X_DRAWOBJ 0, 0
X_DRAWOBJ 1, 0

SHOWSCREEN

WEND

33
inspired by the question of Stu_C about the operation method of SPLITSTR

http://www.glbasic.com/forum/index.php?topic=2384.0

I've tried to implement a modified version of SPLITSTR, which will not ignore multiple splitter without any data between them. As AndyH noted, this could be useful for processing csv files.

The first version of TrueSplitStr, that's how I called it ;), looks like follows:

Code: (glbasic) [Select]
FUNCTION TrueSplitStr: p_text$, p_split$[], p_delim$

LOCAL l_len_text                          // length of text$
LOCAL l_ctext                             // loop counter
LOCAL l_char$                             // one character of text$
LOCAL l_item$                             // to add to p_split$[]

l_len_text = LEN(p_text$)

DIM p_split$[0]                           // initialize p_spit$[]

FOR l_ctext = 0 TO l_len_text - 1
l_char$ = MID$(p_text$, l_ctext, 1)   // get the next character of text$
IF INSTR(p_delim$, l_char$) = -1      // is it a delimiter?
l_item$ = l_item$ + l_char$       // no, so add it to the next item
ELSE
DIMPUSH p_split$[], l_item$       // yes then continue with the next word
l_item$ = ""
ENDIF
NEXT
DIMPUSH p_split$[], l_item$

RETURN LEN(p_split$[])                    // return the number of strings

ENDFUNCTION

The doubts about performance mentioned in the thread named above, are not unjustified. This version of SplitStr needs 2-3 times more runtime than the original command. One reason is the DIMPUSH command. Depending on the count of needed array elements it must allocate new memory and must preserve the existing array content.

But if I can calculate the number of needed array elements, I'm able to create the array with only one DIM command and fill the array within the FOR-NEXT-loop.

This coding is bigger but much faster than the first version:

Code: (glbasic) [Select]
FUNCTION TrueSplitStr_Fast: p_text$, p_split$[], p_delim$

LOCAL l_len_text                          // length of text$
LOCAL l_ctext                             // loop counter
LOCAL l_char$                             // one character of text$
LOCAL l_item$                             // to add to p_split$[]
LOCAL l_count                             // number of entries needed for p_split$[]
LOCAL l_idx                               // current index of p_split$[]

l_len_text = LEN(p_text$)

// first get the needed number of entries for p_split$[]
FOR l_ctext = 0 TO l_len_text - 1
IF INSTR(p_delim$, MID$(p_text$, l_ctext, 1)) > -1
INC l_count, 1
ENDIF
NEXT
REDIM p_split$[l_count + 1]

// now do the split
FOR l_ctext = 0 TO l_len_text - 1
l_char$ = MID$(p_text$, l_ctext, 1)   // get the next character of text$
IF INSTR(p_delim$, l_char$) = -1      // is it a delimiter?
l_item$ = l_item$ + l_char$       // no, so add it to the next item
ELSE
p_split$[l_idx] = l_item$         // yes then continue with the next word
l_item$ = ""
INC l_idx, 1
ENDIF
NEXT
p_split$[l_idx] = l_item$

RETURN LEN(p_split$[])                    // return the number of strings

ENDFUNCTION

First the size of the array is calculated. Then it's filled.

The following coding shows all three versions of SplitStr

- SPLITSTR command of GLBasic
- slow version of TrueSplitStr
- fast version of TrueSplitStr

and shows the needed time. As an example I've attached a csv file (example.txt), which is composed by 1000 rows and 10 columns. The commented function debugshow shows the results in the debug window (activate debug mode first).

The results on my computer were: The slow variant fo TrueSplitStr was 2-3 times slower than the SPLITSTR command, the fast variant about 50% slower.

Code: (glbasic) [Select]
LOCAL text$[]
LOCAL split$[]
LOCAL line$

// read a sample csv file
OPENFILE(1, "example.csv", TRUE)
WHILE ENDOFFILE(1) = FALSE
READLINE 1, line$
DIMPUSH text$[], line$
WEND

LOCAL time_start, time_end
LOCAL time_splitstr, time_splitstrslow, time_splitstrfast
LOCAL i, total, count

total = LEN(text$[])

// first test with original SPLITSTR command
time_start = GETTIMERALL()
FOR i = 0 TO total - 1
count = SPLITSTR(text$[i], split$[], ",")
//debugshow(split$[])                     // activate debug mode to see the results in debug window
NEXT
time_end = GETTIMERALL()
time_splitstr = time_end - time_start

// now the "slow" variant of TrueSplitStr
time_start = GETTIMERALL()
FOR i = 0 TO total - 1
count = TrueSplitStr_Slow(text$[i], split$[], ",")
//debugshow(split$[])                     // activate debug mode to see the results in debug window
NEXT
time_end = GETTIMERALL()
time_splitstrslow = time_end - time_start

// and finally the "fast" variant of TrueSplitStr
time_start = GETTIMERALL()
FOR i = 0 TO total - 1
count = TrueSplitStr_Fast(text$[i], split$[], ",")
//debugshow(split$[])                     // activate debug mode to see the results in debug window
NEXT
time_end = GETTIMERALL()
time_splitstrfast = time_end - time_start

PRINT "Original SPLITSTR command     :" + time_splitstr, 0, 0
PRINT "Slow TrueSplitStr function    :" + time_splitstrslow, 0, 30
PRINT "Fast TrueSplitStr function    :" + time_splitstrfast, 0, 60
SHOWSCREEN
KEYWAIT


FUNCTION TrueSplitStr_Slow: p_text$, p_split$[], p_delim$

LOCAL l_len_text                          // length of text$
LOCAL l_ctext                             // loop counter
LOCAL l_char$                             // one character of text$
LOCAL l_item$                             // to add to p_split$[]

l_len_text = LEN(p_text$)

DIM p_split$[0]                           // initialize p_spit$[]

FOR l_ctext = 0 TO l_len_text - 1
l_char$ = MID$(p_text$, l_ctext, 1)   // get the next character of text$
IF INSTR(p_delim$, l_char$) = -1      // is it a delimiter?
l_item$ = l_item$ + l_char$       // no, so add it to the next item
ELSE
DIMPUSH p_split$[], l_item$       // yes then continue with the next word
l_item$ = ""
ENDIF
NEXT
DIMPUSH p_split$[], l_item$

RETURN LEN(p_split$[])                    // return the number of strings

ENDFUNCTION


FUNCTION TrueSplitStr_Fast: p_text$, p_split$[], p_delim$

LOCAL l_len_text                          // length of text$
LOCAL l_ctext                             // loop counter
LOCAL l_char$                             // one character of text$
LOCAL l_item$                             // to add to p_split$[]
LOCAL l_count                             // number of entries needed for p_split$[]
LOCAL l_idx                               // current index of p_split$[]

l_len_text = LEN(p_text$)

// first get the needed number of entries for p_split$[]
FOR l_ctext = 0 TO l_len_text - 1
IF INSTR(p_delim$, MID$(p_text$, l_ctext, 1)) > -1
INC l_count, 1
ENDIF
NEXT
REDIM p_split$[l_count + 1]

// now do the split
FOR l_ctext = 0 TO l_len_text - 1
l_char$ = MID$(p_text$, l_ctext, 1)   // get the next character of text$
IF INSTR(p_delim$, l_char$) = -1      // is it a delimiter?
l_item$ = l_item$ + l_char$       // no, so add it to the next item
ELSE
p_split$[l_idx] = l_item$         // yes then continue with the next word
l_item$ = ""
INC l_idx, 1
ENDIF
NEXT
p_split$[l_idx] = l_item$

RETURN LEN(p_split$[])                    // return the number of strings

ENDFUNCTION


FUNCTION debugshow: p_split$[]
LOCAL i
FOR i = 0 TO LEN(p_split$[]) - 1
DEBUG p_split$[i] + "\t"
NEXT
DEBUG "\n"
ENDFUNCTION


[attachment deleted by admin]

34
inspiriert durch die Frage von Stu_C nach der Arbeitsweise von SPLITSTR

http://www.glbasic.com/forum/index.php?topic=2384.0

habe ich mich an eine modifizierte Version von SPLITSTR gewagt, die doppelte Trennzeichen nicht ignoriert, sondern auch in den Splitprozess miteinbezieht. Wie AndyH richtig bemerkte, kann das z.B. für die Verarbeitung von CSV-Dateien sinnvoll sein.

Eine erste Version von TrueSplitStr, wie ich die Funktion anmaßenderweise mal genannt habe, sieht folgendermaßen aus:

Code: (glbasic) [Select]
FUNCTION TrueSplitStr: p_text$, p_split$[], p_delim$

LOCAL l_len_text                          // length of text$
LOCAL l_ctext                             // loop counter
LOCAL l_char$                             // one character of text$
LOCAL l_item$                             // to add to p_split$[]

l_len_text = LEN(p_text$)

DIM p_split$[0]                           // initialize p_spit$[]

FOR l_ctext = 0 TO l_len_text - 1
l_char$ = MID$(p_text$, l_ctext, 1)   // get the next character of text$
IF INSTR(p_delim$, l_char$) = -1      // is it a delimiter?
l_item$ = l_item$ + l_char$       // no, so add it to the next item
ELSE
DIMPUSH p_split$[], l_item$       // yes then continue with the next word
l_item$ = ""
ENDIF
NEXT
DIMPUSH p_split$[], l_item$

RETURN LEN(p_split$[])                    // return the number of strings

ENDFUNCTION

Die Bedenken zur Performance, die im obigen Thread auch genannt wurden, sind nicht ganz unberechtigt. Diese Version von SplitStr benötigt etwa 2-3 mal so viel Zeit wie das Original. Als leichte Bremse erweist sich der DIMPUSH-Befehl, der je nach Anzahl der Array-Elemente immer mal wieder neuen Speicher reservieren und dabei auch die bisherigen Array-Elemente erhalten (sprich umschaufeln) muss.

Wenn man jedoch im Vorfeld die benötigte Anzahl der Arrayelemente schon berechnet, kann man das Array mit einem einzigen DIM-Befehl erzeugen und dann innerhalb der FOR-NEXT-Schleife füllen.

Das Coding ist zwar etwas größer, aber auch deutlich schneller als die erste Version:

Code: (glbasic) [Select]
FUNCTION TrueSplitStr_Fast: p_text$, p_split$[], p_delim$

LOCAL l_len_text                          // length of text$
LOCAL l_ctext                             // loop counter
LOCAL l_char$                             // one character of text$
LOCAL l_item$                             // to add to p_split$[]
LOCAL l_count                             // number of entries needed for p_split$[]
LOCAL l_idx                               // current index of p_split$[]

l_len_text = LEN(p_text$)

// first get the needed number of entries for p_split$[]
FOR l_ctext = 0 TO l_len_text - 1
IF INSTR(p_delim$, MID$(p_text$, l_ctext, 1)) > -1
INC l_count, 1
ENDIF
NEXT
REDIM p_split$[l_count + 1]

// now do the split
FOR l_ctext = 0 TO l_len_text - 1
l_char$ = MID$(p_text$, l_ctext, 1)   // get the next character of text$
IF INSTR(p_delim$, l_char$) = -1      // is it a delimiter?
l_item$ = l_item$ + l_char$       // no, so add it to the next item
ELSE
p_split$[l_idx] = l_item$         // yes then continue with the next word
l_item$ = ""
INC l_idx, 1
ENDIF
NEXT
p_split$[l_idx] = l_item$ 

RETURN LEN(p_split$[])                    // return the number of strings

ENDFUNCTION

Hier wird zunächst die erforderliche Größe des Arrays errechnet und anschließend das erzeugte Array gefüllt.

Das folgende Coding stellt alle drei Versionen von SplitStr, also

- SPLITSTR - Befehl von GLBasic
- langsame Version von TrueSplitStr
- schnelle Version von TrueSplitStr

gegenüber und gibt die verbrauchten Zeiten an. Als Beispiel habe ich eine CSV-Datei beigefügt, die aus 1000 Zeilen mal 10 Spalten besteht. Die hier auskommentierte Funktion debugshow kann bei aktiviertem Debug-Modus dazu genutzt werden, die Ergebnisse im Debug-Fenster auszugeben.

Die Ergebnisse bei mir waren in etwa so, daß die langsame Variante von TrueSplitStr etwa 2-3 mal langsamer war als der SPLITSTR-Befehl, die schnellere Variante etwa 50% langsamer.

Code: (glbasic) [Select]


LOCAL text$[]
LOCAL split$[]
LOCAL line$

// read a sample csv file
OPENFILE(1, "example.txt", TRUE)
WHILE ENDOFFILE(1) = FALSE
READLINE 1, line$
DIMPUSH text$[], line$
WEND

LOCAL time_start, time_end
LOCAL time_splitstr, time_splitstrslow, time_splitstrfast
LOCAL i, total, count

total = LEN(text$[])

// first test with original SPLITSTR command
time_start = GETTIMERALL()
FOR i = 0 TO total - 1
count = SPLITSTR(text$[i], split$[], ",")
//debugshow(split$[])                     // activate debug mode to see the results in debug window
NEXT
time_end = GETTIMERALL()
time_splitstr = time_end - time_start

// now the "slow" variant of TrueSplitStr
time_start = GETTIMERALL()
FOR i = 0 TO total - 1
count = TrueSplitStr_Slow(text$[i], split$[], ",")
//debugshow(split$[])                     // activate debug mode to see the results in debug window
NEXT
time_end = GETTIMERALL()
time_splitstrslow = time_end - time_start

// and finally the "fast" variant of TrueSplitStr
time_start = GETTIMERALL()
FOR i = 0 TO total - 1
count = TrueSplitStr_Fast(text$[i], split$[], ",")
//debugshow(split$[])                     // activate debug mode to see the results in debug window
NEXT
time_end = GETTIMERALL()
time_splitstrfast = time_end - time_start

PRINT "Original SPLITSTR command     :" + time_splitstr, 0, 0
PRINT "Slow TrueSplitStr function    :" + time_splitstrslow, 0, 30
PRINT "Fast TrueSplitStr function    :" + time_splitstrfast, 0, 60
SHOWSCREEN
KEYWAIT


FUNCTION TrueSplitStr_Slow: p_text$, p_split$[], p_delim$

LOCAL l_len_text                          // length of text$
LOCAL l_ctext                             // loop counter
LOCAL l_char$                             // one character of text$
LOCAL l_item$                             // to add to p_split$[]

l_len_text = LEN(p_text$)

DIM p_split$[0]                           // initialize p_spit$[]

FOR l_ctext = 0 TO l_len_text - 1
l_char$ = MID$(p_text$, l_ctext, 1)   // get the next character of text$
IF INSTR(p_delim$, l_char$) = -1      // is it a delimiter?
l_item$ = l_item$ + l_char$       // no, so add it to the next item
ELSE
DIMPUSH p_split$[], l_item$       // yes then continue with the next word
l_item$ = ""
ENDIF
NEXT
DIMPUSH p_split$[], l_item$

RETURN LEN(p_split$[])                    // return the number of strings

ENDFUNCTION


FUNCTION TrueSplitStr_Fast: p_text$, p_split$[], p_delim$

LOCAL l_len_text                          // length of text$
LOCAL l_ctext                             // loop counter
LOCAL l_char$                             // one character of text$
LOCAL l_item$                             // to add to p_split$[]
LOCAL l_count                             // number of entries needed for p_split$[]
LOCAL l_idx                               // current index of p_split$[]

l_len_text = LEN(p_text$)

// first get the needed number of entries for p_split$[]
FOR l_ctext = 0 TO l_len_text - 1
IF INSTR(p_delim$, MID$(p_text$, l_ctext, 1)) > -1
INC l_count, 1
ENDIF
NEXT
REDIM p_split$[l_count + 1]

// now do the split
FOR l_ctext = 0 TO l_len_text - 1
l_char$ = MID$(p_text$, l_ctext, 1)   // get the next character of text$
IF INSTR(p_delim$, l_char$) = -1      // is it a delimiter?
l_item$ = l_item$ + l_char$       // no, so add it to the next item
ELSE
p_split$[l_idx] = l_item$         // yes then continue with the next word
l_item$ = ""
INC l_idx, 1
ENDIF
NEXT
p_split$[l_idx] = l_item$

RETURN LEN(p_split$[])                    // return the number of strings

ENDFUNCTION


FUNCTION debugshow: p_split$[]
LOCAL i
FOR i = 0 TO LEN(p_split$[]) - 1
DEBUG p_split$[i] + "\t"
NEXT
DEBUG "\n"
ENDFUNCTION


[attachment deleted by admin]

35
Code Snippets / Binäre Zahlen - Bin$-Funktion
« on: 2008-Sep-29 »
kann manchmal nützlich sein, wenn man mit Bits rumspielt
im optionalen Parameter p_bytes kann man mitgeben, wie groß die zurückgelieferte Binärzahl ist 1 Byte = 8 Zeichen, 2 Byte = 16 Zeichen etc. Voreinstellung ist 4 Byte für 32 Bit-Zahlen

Code: (glbasic) [Select]
PRINT Bin$(2594585588), 0, 0
SHOWSCREEN
KEYWAIT

// ------------------------------------------------------------- //
// ---  BIN$  ---
// paramerters: p_num   number to convert in binary format
//               p_bytes number of bytes, on 32 bit CPU 4 byte
//                      is maximum
// ------------------------------------------------------------- //
FUNCTION Bin$: p_num, p_bytes = 4
   
   LOCAL l_length = p_bytes * 8
   LOCAL l_counter
   LOCAL l_result$
   
   FOR l_counter = l_length - 1 TO 0 STEP -1
      IF bAND(p_num,POW(2, l_counter))
         l_result$ = l_result$ + "1"
      ELSE
         l_result$ = l_result$ + "0"
      ENDIF
   NEXT
   
   RETURN l_result$

ENDFUNCTION // BIN$

36
GLBasic - de / GLBasic - Buch
« on: 2008-Sep-29 »
erstaunlich, was man im Netz alles so findet, hier ein komplettes Buch über GLBasic.

http://www.terpsichori-ev.de/GLBasic.pdf

Autor hab ich jetzt auf die Schnelle nicht herausgefunden (Gernot daselbst?). Die Homepage befaßt sich eigentlich mit traditionellen griechischen Tänzen.

K.A. warum man so ein tolles Werk geheimhält ;) ... oder ist es etwa schon bekannt?

37
(If you find extreme errors in my poor english please let me know)

Surely to compare this with Stephen Hawkings book about the time is overstated, but in my borderless modesty I couldn't catch something more adequated. ;)

As Gernot told us, after some questions about object orientated enhancements for GLBasic, that he wouldn't implement them and who really need this, should do it with INLINE, I took this words to my heart and tried to realize some OOP in GLBasic. One thing before we start: I'm not an expert in C++ and object orientated programming. So if you see some big violations against C++ or OOP dogmas, please tell it to me in a gentle way.

In principle GLBasic already works with classes. For example if you compile the following coding:


TYPE t_anything
   x
   y
ENDTYPE

LOCAL something AS t_anything


GLBasic will generate this C++ code.
(you'll find it in C:\Documents and Settings\<computername>\Local Settings\Temp\glbasic\gc_temp_class.h, please replace <computername> with your own one)


class t_anything
{
public:
   DGInt x;
   DGInt y;
   t_anything()
   {
      x = 0;
      y = 0;
   }
   t_anything(const t_anything& _in_)
   {*this = _in_;}
   t_anything& operator=(const t_anything& _in_)
   {
      this->x = _in_.x;
      this->y = _in_.y;
   return *this;
   }

};


A class was created from the GLBasic TYPE with the attributes x and y, two constructors to initialize the data and an overloaded = operator. The = operator must be overloaded to allow assignments like


LOCAL something AS t_anything
LOCAL something_new AS t_anything

something.x = 5
something.y = 7
something_new = something


The object creation of the class is done in the main file gpc_temp0.cpp within the function __MainGameSub__. The command is "REGISTER_VAR(t_anything, something);"
In principle all attributes are defined as "public". In order to change this we can define our own classes with INLINE.


So now we have the pattern to implement own classes. The following small example creates a class and plays something with the attributes.

Code: (glbasic) [Select]
INLINE
// class definition
class canything
{
private:
DGInt x;
DGInt y;

public:
// standard constructor
canything()
{
x = 0;
y = 0;
}

DGInt getx() { return x; }
DGInt gety() { return y; }
void setpos(const DGInt new_x, const DGInt new_y)
{
x = new_x;
y = new_y;
}

};
ENDINLINE

INLINE
// create an object
canything something;
ENDINLINE

LOCAL scrx, scry, x, y
GETSCREENSIZE scrx, scry

WHILE TRUE
INLINE
//set x and y accidently
something.setpos(RND(scrx), RND(scry));
x = something.getx();
y = something.gety();
PRINT(CGStr("X:") + x + CGStr(" - Y:") + y, x, y);
SHOWSCREEN();
SLEEP(1500);
ENDINLINE
WEND

Ok this works. Of course it is not very sensible.
In this example the functions (or methods) are defined within the class definition. If doing so, the class methods are defind implicit as inline methods (as far as I know). This is no handicap for this example. But if a method is bigger and is called several times in the coding it could bloat the final coding. We can try to change our class definition, so that within the class only the method headers are shown and the implementation outside ...

Code: (glbasic) [Select]
INLINE
// class definition
class canything
{
private:
DGInt x;
DGInt y;

public:
canything();
DGInt getx();
DGInt gety();
void setpos(const DGInt new_x, const DGInt new_y);
};

canything::canything()
{
x = 0;
y = 0;
}

DGInt canything::getx() { return x; }
DGInt canything::gety() { return y; }
void canything::setpos(const DGInt new_x, const DGInt new_y)
{
x = new_x;
y = new_y;
}
ENDINLINE

... and we're facing a bad surprise. The compiler tells us several error messages, which are not very understandable on the fist view. On the second view (in my case the second view came from Gernot) the reason for the error messages are more clear. The implementation of the class methods was done within the main function of the coding, which is generated by GLBasic, more detailed, within the function __MainGameSub__, which is called from the WinMain function. Thus the class methods are implemented within a function and I don't know a compiler which allows that.

So I'll have to find another way to make the class definition global. You'll find several examples in the forum on how to do this.


// empty function in order to end the main function
FUNCTION __dummy:
ENDFUNCTION

INLINE
// here we can define our class
ENDINLINE


Now we're facing the next problem. Now the class is outside any function, but anyhow it isn't known within the main function. It's because the class def. is set AFTER the main function within the C++ coding which is generated by GLBasic. We can manage this by  creating our own main function, which is defined after INLINE. Schematically our coding looks as follows:


// call our own main function
MyMain()

// end original main function here
FUNCTION __dummy:
ENDFUNCTION

// class definition and implementation
INLINE
class...
ENDINLINE

// own main function
FUNCTION MyMain:
   ...
ENDFUNCTION



Once more the complete coding:

Code: (glbasic) [Select]
// calling own main function
MyMain()


FUNCTION __dummy:
ENDFUNCTION

INLINE
// class definition
class canything
{
private:
DGInt x;
DGInt y;

public:
canything();
DGInt getx();
DGInt gety();
void setpos(const DGInt new_x, const DGInt new_y);
};

// Implementation of the class methods
canything::canything()
{
x = 0;
y = 0;
}

DGInt canything::getx() { return x; }
DGInt canything::gety() { return y; }
void canything::setpos(const DGInt new_x, const DGInt new_y)
{
x = new_x;
y = new_y;
}
ENDINLINE

// ------------------------------------------------------------- //
// ---  MYMAIN  ---
// ------------------------------------------------------------- //
FUNCTION MyMain:

INLINE
// create an object
canything something;
ENDINLINE

LOCAL scrx, scry, x, y
GETSCREENSIZE scrx, scry

WHILE TRUE
INLINE
// set x and y accidently
something.setpos(RND(scrx), RND(scry));
x = something.getx();
y = something.gety();
PRINT(CGStr("X:") + x + CGStr(" - Y:") + y, x, y);
SHOWSCREEN();
SLEEP(1500);
ENDINLINE
WEND

ENDFUNCTION // MYMAIN
    Fine. Now we have the knowledge for further mistakes. I mean arrays. Often not only one variable of a TYPE (respectively an object) is needed but an array. As we're programming in C++ now there will be several ways to realize that.

    • with C++ arrays (canything something[20]; )
    • with linked lists
    • with an own array class
    • or ... we are clever an are using the functionallity of GLBasic

We will extend the example above by creating an array of class canything. This can be done very simple with the command


DGArray<canything> arr_something;


as described in the GLBasic help fle, DGArray is a template class. Thus we'll have to tell it which kind of array we want (here <canything>). With this array we can use all array functions of GLBasic like DIM, REDIM, DIMPUSH and DIMDEL.

Now here the changed main function:

Code: (glbasic) [Select]
// ------------------------------------------------------------- //
// ---  MYMAIN  ---
// ------------------------------------------------------------- //
FUNCTION MyMain:

LOCAL i

INLINE
// create an object
canything something;
DGArray<canything> arr_something;
ENDINLINE

LOCAL scrx, scry, x, y
GETSCREENSIZE scrx, scry

// create 10 elements
FOR i = 0 TO 9
INLINE
something.setpos(RND(scrx), RND(scry));
DIMPUSH(arr_something, something);
ENDINLINE
NEXT

WHILE TRUE
INLINE
// display array in a FOREACH loop
for(i = 0; i <= LEN(arr_something()) - 1; )
{
canything& a = arr_something(i++);
x = a.getx();
y = a.gety();
PRINT(CGStr("X:") + x + CGStr(" - Y:") + y, x, y);
}
SHOWSCREEN();
ENDINLINE
WEND

ENDFUNCTION // MYMAIN

One word to the FOREACH loop within WILE-WEND. I've "learned" this by looking on how GLBasic is translating a FOREACH loop in C++ coding. You see that the loop counter "i" is not incremented in the for statement but later within the loop. This is important. Otherwise the loop will start with the second element and will cause an error when trying to process the last element which is out of bounds of the array.

The command


canything& a = arr_something(i++);


give us the address of the current array element in the varialbe a, so we can call all class methods with it. The DELETE statement of GLBasic will be translated to the commands


DIMDEL(arr_something, --i);
continue;


Ok, enough for now.

Result:
It made a lot of fun working with INLINE and classes. If it make sense ... I really don't know. Ok we'll have encapsulation of the data but there are so many traps when programming in C++ and it makes much more effort working in that way comparing the programming with GLBasic only. Anyway at the moment I enjoy playing with it.


38
Der Vergleich mit Stephen Hawkings Büchlein über die Zeit ist natürlich stark übertrieben, aber in meiner grenzenlosen Bescheidenheit ist mir nichts passenderes eingefallen ;).

Nachdem Gernot diversen Anfragen zu objektorientierten Erweiterungen in GLBasic mehr oder minder ein Abfuhr erteilt hat und er meinte, wer es unbedingt haben wolle, solle sich mit INLINE etwas basteln, habe ich mir diesen Rat zu Herzen genommen und probiert, genau das in GLBasic umzusetzen. Eines gleich vorweg: Ich bin alles andere als fit in Sachen C++ und Objektorientierung. Wer also in den kommenden Ausführungen grobe Verstöße gegen C++ - oder OOP-Dogmen erkennt, darf mir das schonend beibringen.

Im Prinzip arbeitet GLBasic schon mit Klassen und Objekten. Wenn man z.B. folgendens Coding kompiliert,


TYPE t_irgendwas
   x
   y
ENDTYPE

LOCAL etwas AS t_irgendwas


wird daraus dieses C++ - Coding generiert:
(zu finden unter C:\Dokumente und Einstellungen\<Rechnername>\Lokale Einstellungen\Temp\glbasic\gpc_temp_class.h, <Rechnername> bitte durch eigenen Rechnernamen ersetzen)


class t_irgendwas
{
public:
   DGInt x;
   DGInt y;
   t_irgendwas()
   {
      x = 0;
      y = 0;
   }
   t_irgendwas(const t_irgendwas& _in_)
   {*this = _in_;}
   t_irgendwas& operator=(const t_irgendwas& _in_)
   {
      this->x = _in_.x;
      this->y = _in_.y;
   return *this;
   }

};


Aus unserem TYPE ist also eine Klasse geworden mit den beiden Attributen x und y, zwei Konstruktoren sowie einem überladenen = - Operator. Der = - Operator muss deshalb überladen werden, damit Zuweisungen wie


LOCAL etwas AS t_irgendwas
LOCAL nochwas AS t_irgendwas

etwas.x = 5
etwas.y = 7
nochwas = etwas


möglich sind. Die Instanzierung der Klasse erfolgt im Hauptprogramm gpc_temp0.cpp innerhalb der Funktion __MainGameSub__. Der Befehl lautet "REGISTER_VAR(t_irgendwas, etwas);"
Alle Attribute der Klasse sind prinzipiell als "public" definiert. Und genau das kann mit der Formulierung von eigenen Klassen mittels INLINE anders gestaltet werden.


Somit haben wir auch schon prinzipiell die Vorlage für die Implementierung von eigenen Klassen. Folgendes kleine Beispiel erstellt eine Klasse mit einigen Methoden, spielt ein wenig damit rum.

Code: (glbasic) [Select]
INLINE
// Klassendefinition
class cirgendwas
{
private:
DGInt x;
DGInt y;

public:
// Standardkonstruktor
cirgendwas()
{
x = 0;
y = 0;
}

DGInt getx() { return x; }
DGInt gety() { return y; }
void setpos(const DGInt new_x, const DGInt new_y)
{
x = new_x;
y = new_y;
}

};
ENDINLINE

INLINE
// ein Objekt anlegen
cirgendwas etwas;
ENDINLINE

LOCAL scrx, scry, x, y
GETSCREENSIZE scrx, scry

WHILE TRUE
INLINE
//x und y zufällig setzen
etwas.setpos(RND(scrx), RND(scry));
x = etwas.getx();
y = etwas.gety();
PRINT(CGStr("X:") + x + CGStr(" - Y:") + y, x, y);
SHOWSCREEN();
SLEEP(1500);
ENDINLINE
WEND

Läuft schon mal, wenn es auch wenig sinnvoll ist.
Im Beispiel sind die Funktionen innerhalb der Klassendefinitionen ausformuliert. Wenn man das so macht, sind diese Klassenmethoden implizit als inline deklariert (soweit ich richtig informiert bin). In diesem Beispiel wäre das sicherlich kein Schaden. Wenn eine Methode jedoch etwas umfangreicher ist und an vielen Stellen im Coding aufgerufen wird, kann dies den Code aufblähen. Also probieren wir doch mal, unsere Klassendefinition so umzustellen, daß in der Klasse nur die Methoden definiert werden und das eigentliche Coding außerhalb ...

Code: (glbasic) [Select]
INLINE
// Klassendefinition
class cirgendwas
{
private:
DGInt x;
DGInt y;

public:
cirgendwas();
DGInt getx();
DGInt gety();
void setpos(const DGInt new_x, const DGInt new_y);
};

cirgendwas::cirgendwas()
{
x = 0;
y = 0;
}

DGInt cirgendwas::getx() { return x; }
DGInt cirgendwas::gety() { return y; }
void cirgendwas::setpos(const DGInt new_x, const DGInt new_y)
{
x = new_x;
y = new_y;
}
ENDINLINE

... und erleben eine böse Überraschung. Der Compiler überhäuft uns mit freundlichen, auf den ersten Blick unverständlichen Fehlermeldungen. Auf den zweiten Blick (in meinem Fall kam der zweite Blick von Gernot) wird dann auch klar, warum die Fehlermeldungen kommen. Die Stelle, an der ich die Klassendefinition vorgenommen habe, liegen im von GLBasic erzeugten C++ - Programm innerhalb der Main-Funktion, genauer innerhalb der Funkton __MainGameSub__, die von der WinMain-Funktion aufgerufen wird. Dadurch, daß ich die Methoden außerhalb der Klassendefinition gesetzt habe, werden diese innerhalb einer Funktion __MainGameSub__ geschrieben. Funktionsdefinitionen innerhalb einer Funktion sieht kaum ein Compiler gern.

Folglich muss ich eine Möglichkeit finden, die Klassendefinition global bekannt zu machen. Wie man das im Programm darstellt, wurde ja schon an vielen Stellen im Forum beschrieben.


// leere Funktion, damit das folgende Coding außerhalb der Main-Funktion liegt
FUNCTION __dummy:
ENDFUNCTION

INLINE
// ab hier kann dann die Klassendefintion + Implementierung liegen
ENDINLINE


Jetzt kommt das nächste Problem. Die Klasse liegt zwar jetzt außerhalb einer Funktion, in der Main-Funktion ist sie jedoch nicht bekannt, weil sie im generierten Coding NACH der Main-Funktion liegt. Man kann sich damit behelfen, daß man sich seine eigene "Main-Funktion" bastelt, die nach dem INLINE-Block liegt. Schematisch ist das Programm dann folgendermaßen aufgebaut.


// Aufruf der eigenen Main-Funktion
MyMain()

// Main beenden mit Dummy-Funktion
FUNCTION __dummy:
ENDFUNCTION

// Klassendefinition
INLINE
class ...
ENDINLINE

// eigene Main-Funktion
FUNCTION MyMain:
 ...
ENDFUNCTION



Hier noch mal das komplette Coding:

Code: (glbasic) [Select]
// Aufruf der eigenen Main-Funktion
MyMain()


FUNCTION __dummy:
ENDFUNCTION

INLINE
// Klassendefinition
class cirgendwas
{
private:
DGInt x;
DGInt y;

public:
cirgendwas();
DGInt getx();
DGInt gety();
void setpos(const DGInt new_x, const DGInt new_y);
};

// Implementierung der Klassenmethoden
cirgendwas::cirgendwas()
{
x = 0;
y = 0;
}

DGInt cirgendwas::getx() { return x; }
DGInt cirgendwas::gety() { return y; }
void cirgendwas::setpos(const DGInt new_x, const DGInt new_y)
{
x = new_x;
y = new_y;
}
ENDINLINE

// ------------------------------------------------------------- //
// ---  MYMAIN  ---
// ------------------------------------------------------------- //
FUNCTION MyMain:

INLINE
// ein Objekt anlegen
cirgendwas etwas;
ENDINLINE

LOCAL scrx, scry, x, y
GETSCREENSIZE scrx, scry

WHILE TRUE
INLINE
//x und y zufällig setzen
etwas.setpos(RND(scrx), RND(scry));
x = etwas.getx();
y = etwas.gety();
PRINT(CGStr("X:") + x + CGStr(" - Y:") + y, x, y);
SHOWSCREEN();
SLEEP(1500);
ENDINLINE
WEND

ENDFUNCTION // MYMAIN


Fein, jetzt haben wir das Rüstzeug für weitere Untaten. Und zwar Array's. Häufig will man ja nicht nur eine Variable eines TYPE's (bzw. eines Objektes) haben, sondern gleich ein ganzes Array davon. Da wir uns jetzt sowieso schon in C++ bewegen, stehen einem diverse Möglichkeiten zur Verfügung

  • man ertellt sich C++ - Arrays (cirgendwas etwas[20]; )
  • man nutzt verkette Listen
  • man schreibt sich eine eigene Array-Klasse
  • oder ... man ist ganz clever und nutzt die Funktionalität von GLBasic

Wir erweitern obiges Beispiel, indem wir ein Array der Klasse cirgendwas anlegen. Dies kann "ganz einfach" mit der Anweisung


DGArray<cirgendwas> arr_etwas;


geschehen. Wie in der GLBasic-Hilfe beschrieben, ist DGArray eine Template-Klasse. Diesem müssen wir natürlich mitteilen, von welchem Typ wir eine Klasse wünschen (<cirgendwas>). Auf dieses Array kann man jetzt alle bekannten GLBasic-Befehle wie DIM, REDIM, DIMPUSH, DIMDEL anwenden.

Hier nur die geänderte MyMain-Funktion:

Code: (glbasic) [Select]
// ------------------------------------------------------------- //
// ---  MYMAIN  ---
// ------------------------------------------------------------- //
FUNCTION MyMain:

LOCAL i

INLINE
// ein Objekt anlegen
cirgendwas etwas;
DGArray<cirgendwas> arr_etwas;
ENDINLINE

LOCAL scrx, scry, x, y
GETSCREENSIZE scrx, scry

// einfach mal 10 Elemente erzeugen
FOR i = 0 TO 9
INLINE
etwas.setpos(RND(scrx), RND(scry));
DIMPUSH(arr_etwas, etwas);
ENDINLINE
NEXT

WHILE TRUE
INLINE
// Array ausgeben mit der "FOREACH-Schleife"
for(i = 0; i <= LEN(arr_etwas()) - 1; )
{
cirgendwas& a = arr_etwas(i++);
x = a.getx();
y = a.gety();
PRINT(CGStr("X:") + x + CGStr(" - Y:") + y, x, y);
}
SHOWSCREEN();
ENDINLINE
WEND

ENDFUNCTION // MYMAIN

Noch ein Wort zur "FOREACH-Schleife" innerhalb von WHILE-WEND. Dieses Konstrukt habe ich abgekupfert, indem ich nachgesehen habe, wie GLBasic eine FOREACH-Schleife in C++ - Code übersetzt. Es fällt auf, daß der Schleifenzähler "i" nicht in der FOR-Anweisung hochgezäht wird, sondern erst innerhalb der Schleife. Das ist wichtig, da sonst die Schleife mit dem zweiten Element beginnen würde und beim letzten Durchlauf auf einen Fehler läuft, da man auf ein nicht existierendes Element des Arrays zugreifen wollte.

Die Anweisung


cirgendwas& a = arr_etwas(i++);


liefert in der lokalen Variablen a die Adresse auf das aktuelle Array-Element. Mit diesem können wir dann wieder ganz normal mit den Klassen-Methoden arbeiten. Das Löschen von Array-Elementen mit DELETE wird übersetzt in die Anweisungen


DIMDEL(arr_etwas, --i);
continue;


In der GLBasic-Hilfe zu INLINE ist beschrieben, daß bei der Verwendung von DGArray die Operatoren "=" und "()" definiert werden müssen. Das obige Beispiel oder auch das von Schranzor (http://www.glbasic.com/forum/index.php?topic=2224.0) sind aber auch ohne überladene Opertoren lauffähig. In welchen Fällen das dann wirklich erforderlich ist, entzieht sich leider meiner Kenntnis.

Jetzt erst einmal genug davon.

Fazit:
Das Rumgefummel mit Klassen und INLINE macht richtig Spaß. Was die Sinnhaftigkeit angeht, bin ich jedoch arg im Zweifel. Was erreicht man durch die ganze Plackerei, die einem die ungezählten Fall-Stricke der C++ - Programmierung in den Weg legen. Datenkapselung. Schön und gut. Ob das jedoch den Aufwand rechtfertigt, kann ich nicht abschließend beantworten. Zur Zeit macht es einfach Spaß, damit rumzuspielen.

P.S.
Wenn Schranzor mich jetzt dazu verdonnert, diesen ganzen Wirsing ins englische zu übersetzen, ist mein Urlaub ganz versaut ;)



39
GLBasic - de / Frage zu #include "datei.h"
« on: 2008-Aug-12 »
ich spiele gerade ein wenig mit INLINE und Klassendefinitionen herum. Das klappt soweit ganz gut, nur wenn ich Klassen in Verbindung mit DGArray verwenden will, habe ich noch einige Probleme, weil die Klassendefinition dann innerhalb von __MainGameSub_ lokal angelegt werden.

Der einzige Weg, wie ich es bisher zu einer fehlerfreien Kompilierung bringen konte, ist, die Klassendefinitionen in eine Header-Datei zu packen und diese z.B. in glb.h einzubinden. Kennt jemand vielleicht noch eine andere Möglichkeit, includes VOR __MainGameSub_ einzubinden?


40
Code Snippets / Timer-Verwaltung
« on: 2008-Aug-02 »
also ehrlich, ich finds nicht gut, wenn hier Posts einfach gelöscht werden. Man kann etwas klarstellen oder auch korrigieren, aber einfach streichen stinkt nach willkürlicher Zensur, und dabei ist's mir erst mal wurscht, ob das aus Sicht der Mods gerechtfertigt scheint oder auch nicht.

aber zum Thema:
Ich finde die Idee von Schranzi mit der Timer-Funktion gar nicht so verkehrt, vor allem wenn man damit mehrere Timer verwalten könnte. Ich habs damit mal versucht, wobei man sogar ohne GLOBALs auskommen kann ;). Dazu müssten die Timer aber erst einmal initialisiert werden. Das geht, indem man als Zeitdauer (millisec) einen negativen Wert mitgibt. Die Timernummern sollten bei Null starten

Code: (glbasic) [Select]
DeltaTimer(0, -1)
DeltaTimer(1, -1)
DeltaTimer(2, -1)

LOCAL counter1, counter2, counter3

WHILE TRUE
IF DeltaTimer(0, 100) THEN INC counter1, 1
IF DeltaTimer(1, 500) THEN INC counter2, 1
IF DeltaTimer(2, 1000) THEN INC counter3, 1
PRINT "1. Timer: " + counter1, 0, 0
PRINT "2. Timer: " + counter2, 0, 20
PRINT "3. Timer: " + counter3, 0, 40
SHOWSCREEN
WEND


// ------------------------------------------------------------- //
// ---  DeltaTimer  ---
// num: internal number of the timer
// millisec: duration ot timer interval
// millisec < 0 --> new timer is created
// ------------------------------------------------------------- //
FUNCTION DeltaTimer: num, millisec

STATIC timer[]

IF millisec <= 0
DIMPUSH timer[], num
timer[LEN(timer[])-1] = GETTIMERALL()
ELSE
IF num > LEN(timer[]) - 1 THEN RETURN FALSE
IF timer[num] < GETTIMERALL()
timer[num] = GETTIMERALL() + millisec
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE

ENDFUNCTION // DELTATIMER

41
GLBasic - de / Fragen zum Update 5.30
« on: 2008-Jun-20 »
zwei Verständnisfragen:

1. Gilt #include "xxx.h" für INLINE .. ENDINLINE ?

2. was steckt hinter libPNG und gilt das nur für OS-X?

42
Block kommentieren arbeitet falsch, wenn die erste Zeile ein Kommentar ist.

Beispiel:
Code: (glbasic) [Select]
// Kommentar
FOR i = 1 TO 9
  PRINT i, 0, i * 20
NEXT

Daraus wird nach STRG + K

Code: (glbasic) [Select]
Kommentar
R i = 1 TO 9
  int i, 0, i * 20
XT

43
Drei GLBasic-Programmier werden von den bösen C++-Jüngern gefangen genommen und sollen gezwungen werden, Lochkarten zu stanzen. Der C++ - Häuptling gibt den dreien eine Chance, diesem Schicksal zu entgehen.

Er hat 5 Federn, 3 weiße und 2 rote. Die drei GLBasic-Programmierer werden hintereinander an drei Pfähle gebunden, sodaß der 3. die beiden vorderen sieht, der zweite noch den 1. und der 1. natürlich keinen. Jeder bekommt per Zufall eine Feder ins Haar gesteckt.

Wenn einer der drei sagen kann, welche Farbe seine Feder hat, so sind sie wieder frei.

Es dauert geschlagene 15 Minuten, bis einer antwortet ... und richtig antwortet.


Frage:
Wer von den dreien antwortet und welche Farbe hat seine Feder?


Wer das schon kennt, bitte nicht ausplärren. Ich habs eben erst gehört und fand das eine ganz nette Knobelei ;)

44
GLBasic - de / Frage zu X_CAMERAUP
« on: 2008-Apr-16 »
Zum Verständnis:

Kann ich bei X_CAMERAUP einen beliebigen Vektor angeben? Also z.B.

X_CAMERAUP 0.5, -0.7, 2*pi

Oder immer nur genau eine Achse in pos. oder neg. Richtung?

45
jaja ich habs noch nicht ganz aufgegeben. Trotz rudimentärem Verständnis für Trigonometrie und ähnliche Schweinereien versuche ich mich immer noch mal in 3D durch den Weltraum zu bewegen, der ja bekanntlich keinen Boden hat. Somit sollte ich meine Kamera ja auch in alle Richtungen frei bewegen können.

Habe mir die Formeln, die hier im Forum dazu geschrieben wurden, immer wieder inhaliert und scheinbar doch nicht wirklich verstanden.

Im folgenden Beispiel ist die Kamera erst einmal auf 0,0,0 gesetzt. Um die Kamera hab ich willkürlich 50 Kuben verstreut (3Dprimitives.gbas aus Common-Ordner einbinden). Nun will ich mit den Cursortasten die Kamera frei bewegen können.

Links - rechts für Bewegung auf X-Z
Oben- unten für Bewegung auf Y-Z

bewege ich nach dem Programmstart die Kamera links-rechts, klappt das wunderbar. Bei oben-unten nur bis Winkel 90 (bzw. 270). dann wird dort die Bewegungsrichtung umgekehrt.

Weiterhin, wenn ich die Kamera z.B. ein wenig nach oben oder unten bewege und dann wieder links-rechts drehe, scheint die Bewegung einer Kurve zu folgen und nicht mehr einer Geraden.

Ich bin ehrlich verwirrt.
Helft einem armen Nicht-Mathematiker.

Code: (glbasic) [Select]
TYPE t_vertex
  x; y; z
ENDTYPE


TYPE t_camera
  pos AS t_vertex
  delta AS t_vertex
  phi                  // angle x-z
  psi                  // angle y-z
ENDTYPE
GLOBAL cam AS t_camera

TYPE t_3dobj
  pos AS t_vertex
  object
  texture
  rotate AS t_vertex
  angle AS t_vertex
ENDTYPE
GLOBAL new3dobj AS t_3dobj
GLOBAL d3dobj[] AS t_3dobj

TYPE t_ship
  speed
  max_speed = 5
ENDTYPE
GLOBAL ship AS t_ship


LOADSPRITE "lava01.bmp", 0

FOR i = 0 TO 50
  CreateCube(i, RAND(20, 50), RGB(128, 128, 128))
  new3dobj.pos.x = RAND(-500, 500)
  new3dobj.pos.y = RAND(-400, 400)
  new3dobj.pos.z = RAND(-400, 500)
  new3dobj.object = i
  new3dobj.texture = 0
  new3dobj.rotate.x = RAND(-10, 10) / 10
  new3dobj.rotate.y = RAND(-10, 10) / 10
  new3dobj.rotate.z = RAND(-10, 10) / 10
  DIMPUSH d3dobj[], new3dobj
NEXT


GLOBAL KEY_LEFT = 203
GLOBAL KEY_RIGHT = 205
GLOBAL KEY_UP = 200
GLOBAL KEY_DOWN = 208
GLOBAL KEY_W = 17          //accellarate
GLOBAL KEY_S = 31          //break

needcalc = TRUE

WHILE TRUE

  WHILE cam.phi > 360; DEC cam.phi, 360; WEND
  WHILE cam.phi < 0; INC cam.phi, 360; WEND
  WHILE cam.psi > 360; DEC cam.psi, 360; WEND
  WHILE cam.psi < 0; INC cam.psi, 360; WEND

  IF KEY(KEY_LEFT)
    DEC cam.phi, 1
    needcalc = TRUE
  ENDIF

  IF KEY(KEY_RIGHT)
    INC cam.phi, 1
    needcalc = TRUE
  ENDIF

  IF KEY(KEY_UP)
    DEC cam.psi, 1
    needcalc = TRUE
  ENDIF

  IF KEY(KEY_DOWN)
    INC cam.psi, 1
    needcalc = TRUE
  ENDIF

  IF needcalc = TRUE
//    IF cam.psi >= 90 AND cam.psi < 270
//      cam.delta.x = COS(cam.phi) * -COS(cam.psi)
//      cam.delta.y = -SIN(cam.psi)
//      cam.delta.z = -SIN(cam.phi) * -COS(cam.psi)
//    ELSE
      cam.delta.x = COS(cam.phi) * COS(cam.psi)
      cam.delta.y = SIN(cam.psi)
      cam.delta.z = -SIN(cam.phi) * COS(cam.psi)
//    ENDIF
    needcalc = FALSE
  ENDIF

  IF KEY(KEY_W)
    INC ship.speed, 0.01
    IF ship.speed > ship.max_speed THEN ship.speed = ship.max_speed
  ENDIF

  IF KEY(KEY_S)
    DEC ship.speed, 0.01
    IF ship.speed < 0 THEN ship.speed = 0
  ENDIF

  INC cam.pos.x, cam.delta.x * ship.speed
  INC cam.pos.y, cam.delta.y * ship.speed
  INC cam.pos.z, cam.delta.z * ship.speed

  X_MAKE3D 1, 1000, 45
  X_CAMERA cam.pos.x, cam.pos.y, cam.pos.z, cam.pos.x+cam.delta.x, cam.pos.y+cam.delta.y, cam.pos.z+cam.delta.z

  FOREACH obj IN d3dobj[]
    X_MOVEMENT obj.pos.x, obj.pos.y, obj.pos.z
    X_ROTATION obj.angle.x, 1, 0, 0
    INC obj.angle.x, obj.rotate.x
    X_ROTATION obj.angle.y, 0, 1, 0
    INC obj.angle.y, obj.rotate.y
    X_ROTATION obj.angle.z, 0, 0, 1
    INC obj.angle.z, obj.rotate.z
    X_SETTEXTURE obj.texture, -1
    X_DRAWOBJ obj.object, 0
  NEXT
  //X_DRAWAXES 0, 0, 0
  X_MAKE2D
  PRINT "Speed: " + (INTEGER(ship.speed * 100)), 0, 0
  PRINT "PHI:   " + cam.phi, 0, 20
  PRINT "PSI:   " + cam.psi, 0, 40
  SHOWSCREEN

WEND


// ------------------------------------------------------------- //
// ---  RAND  ---
// ------------------------------------------------------------- //
FUNCTION RAND: minimum, maximum
  RETURN minimum + RND(-minimum + maximum)
ENDFUNCTION // RAND

Pages: 1 2 [3] 4 5 6