Back to Backgammon

Rok temu zacząłem tworzyć grę w oparciu o OpenGL. Grą tą był Backgammon, jedna z najstarszych gier świata. Chciałem stworzyć wszystko w 3d i zachować jak największy realizm, co okazało się trudnym zadaniem dla początkującego programisty tego typu aplikacji.

Po kilku miesiącach przerwy zasiadłem do pracy ponownie. Zacząłem bawić się silnikiem Physx firmy NVidia, aby gra była bardziej realna. Jak to bywa, co chwilę napotykam na problemy, z którymi męczę się przez kilka godzin, a rozwiązanie okazuje się banalne 🙂 Poniżej 2 filmiki z etapów tworzenia gry 🙂 To dopiero sam początek, więc za bardzo nie ma czym się zachwycać, ale pokazują one mniej więcej, jak będzie wyglądać gra.

Poniższe filmiki pokazują działanie silnika fizycznego do obsługi kostek. Na razie nie są brane pod uwagę kolizje z planszą, a jedynie pomiędzy kostkami. Kolejnym etapem są kolizje z planszą oraz z pionkami.

Etap 1:

Etap 2:

Nie wiem kiedy powstanie etap 3, ale mam nadzieję, że niedługo…

CUDA – przykład (dodawanie wektorów)

Na początek taki szkolny przykład dodawanie do siebie dwóch tablic wektorów.

Dodawanie kolejnych par wektorów odbywa się na karcie graficznej. Odpowiada za to funkcja vecAdd z kwalifikatorem global. Kwalifikator ten oznacza, że funkcja może być wykonywana zarówno jako host (CPU) jak i device (GPU).

Prosty kod odpowiadający za dodawanie 2 wektorów

 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
#include <stdio.h>
#include <cutil.h>
__global__ void vecAdd (float3 a[10], float3 b[10]) {
    int i = threadIdx.x;
    a[i].x += b[i].x;
    a[i].y += b[i].y;
    a[i].z += b[i].z;
}

int main(int argc, char** argv) {
    CUT_DEVICE_INIT(argc, argv); // inicjalizacja GPU
    float3 *a_h, *b_h, *a_d, *b_d, *result;
    int arraySize = 10;
    int sizeInBytes = arraySize * sizeof(float3);
    a_h = (float3*)malloc(sizeInBytes); // inicjalizacja tablic (na hoscie)
    b_h = (float3*)malloc(sizeInBytes);
    result = (float3*)malloc(sizeInBytes);
    CUDA_SAFE_CALL(cudaMalloc((void**)&a_d, sizeInBytes)); // inicjalizacja tablic na GPU
    CUDA_SAFE_CALL(cudaMalloc((void**)&b_d, sizeInBytes));
    for(int i = 0; i < arraySize; i++) { // przykładowe dane
    a_h[i].x = 100.0f + i;
    a_h[i].y = 200.0f + i;
    a_h[i].z = 300.0f + i;
    b_h[i].x = 50.0f + i;
    b_h[i].y = 50.0f + i;
    b_h[i].z = 50.0f + i;

    CUDA_SAFE_CALL(cudaMemcpy(a_d, a_h, sizeInBytes, cudaMemcpyHostToDevice));
    // kopiujemy tablicę z hosta do GPU
    CUDA_SAFE_CALL(cudaMemcpy(b_d, b_h, sizeInBytes, cudaMemcpyHostToDevice));
    vecAdd<<<1, arraySize>>>(a_d, b_d); // wykonujemy dodawanie
    CUDA_SAFE_CALL(cudaMemcpy(result, a_d, sizeInBytes, cudaMemcpyDeviceToHost));
    // kopiujemy z powrotem
    for(int i = 0; i < arraySize; i++){
        printf("Wartość %d.elementu %f, %f, %f \n", i, result[i].x,
        result[i].y, result[i].z); // wyswietlamy :)
    }
    CUT_EXIT(0, NULL); // koniec
}

Wydaje się proste :)

W przyszłości postaram się opisać po kolei wszystkie szczegóły.