gint compatibility

This commit is contained in:
Lephenixnoir 2021-06-18 15:09:35 +02:00
parent 7d08a2ffef
commit dbaf2ae8c2
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
6 changed files with 162 additions and 32 deletions

View File

@ -44,6 +44,8 @@ target_include_directories(${PROJECT_NAME} PRIVATE "${FXSDK_COMPILER_INSTALL}/in
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Os -fno-use-cxa-atexit)
target_link_options(${PROJECT_NAME} PRIVATE -Wl,-Map=map)
target_link_libraries(${PROJECT_NAME} Gint::Gint -lopenlibm -lc -lsupc++)
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)

115
LEPHE.md
View File

@ -1,6 +1,12 @@
Changements faits par Lephe' pour que ça compile :
Voici la liste (chronologique) des changements que j'ai faits pour que ça compile.
## Dans CMakeLists.txt
Certains changements sont dûs à la nature « incomplète » du programme et j'ai rajouté des initialisations, liens, etc. que tu voudras peut-être organiser différemment. Cherche "(LEPHE)" dans les commentaires pour les trouver.
*Je n'ai pas fait le quick sort.*
## Élimination des warnings
Tout ça se fait dans CMakeLists.txt.
* Ajouté `-Wp,-w` à src/map.cpp ; ça indique à GCC de passer `-w` au préprocesseur, ce qui désactive (entre autres) les avertissements de redéfinition de macros pour ce fichier (normalement il faut `#undef` les macros avant de les redéfinir).
@ -8,9 +14,9 @@ Pour information: `-W<lettre>,<options>` passe les options (séparées par des v
* Ajouté `-Wno-missing-field-initializers` à src/map.cpp, ce qui désactive les avertissements pour les champs non initialisés dans les structures ; attention du coup, si tu en oublies des importants personne ne te le dira.
## Dans src/map.cpp et src/windmill.cpp
## Déclaration externes des textures constantes
* Rendu les textures (`tex_white`, `tex_black`, etc) non-constantes
* Rendu les textures (`tex_white`, `tex_black`, etc) non-constantes :
- Dans src/map.cpp: `Texture tex_white = {};`
- Dans src/windmill.cpp `extern Texture tex_white;`
@ -18,15 +24,15 @@ C'est parce qu'en C++ `const` implique *internal linkage* (comme `static`), et d
En déclarant les textures non-constantes les noms sont exportés comme il faut et se retrouvent bien. Du coup les 4 textures arrivent dans la section `.bss` (RAM initialisée à 0) au lieu de `.rodata` (ROM) ; si c'est un problème et à défaut d'une autre solution, tu peux mettre `GSECTION(".rodata")` (de `<gint/defs/attributes.h>`) devant le `Texture` et ça les déplacera dans leur section d'origine.
## Dans src/windmill.cpp
## Définition de render_triangle_white
* Décommenté la vieille fonction `render_triangle_white`. J'ai vu que `render_triangle_black` avait été redéfinie, celle-là je n'y ai pas touché.
* Décommenté la vieille fonction `render_triangle_white` dans src/windmill.cpp. J'ai vu que `render_triangle_black` avait été redéfinie (réécrite ?), celle-là je n'y ai pas touché.
## Test
À ce stade ça compile et le programme ne crashe pas ; l'écran titre a juste une image de titre, et ensuite quand j'appuie sur EXE, écran noir. Pas moyen de sortir, je RESET. Manifestement c'est normal vu la tête de src/main.cpp.
À ce stade ça compile et le programme ne crashe pas ; l'écran titre a juste une image de titre, et ensuite quand j'appuie sur EXE, écran noir. Pas moyen de sortir, je RESET.
## Dans src/windmill.cpp and src/windmill.hpp
## Activation du code qui accède à la VRAM
* Décommenté tout le code concernant la VRAM ; la VRAM est accessible par la variable `gint_vram` déclarée dans `<gint/display.h>`. Sur Graph mono ce n'est pas un tableau de char mais un tableau de `uint32_t` donc attention ! Quand tu écris `char *vram = ...` ça ne pose pas de problème, mais `gint_vram[i]` contrôle 32 pixels d'un coup.
@ -40,6 +46,97 @@ vram[(ground.y << 4) + (ground.x>>3)] |= mask;
## Test
Pareil.
Pareil. J'ai remarqué que le malloc dans `Scene_Map::load_map` échoue parce que `game.map->list_object_length` n'est pas initialisé et contient donc une valeur ridicule, et donc le programme gèle pendant l'initialisation de la scène.
## Ajout de la fonction de debugging
* Implémenté `debug_pop()` dans src/main.cpp et réactivé ses différents usages (sauf les collisions).
Ça c'est pour éviter qu'une allocation échouée ne passe inaperçue. Je l'ai modifié pour accepter non seulement un message mais aussi n'importe quoi qui passe à `printf()`, histoire d'afficher la taille des allocations qui échouent.
## Initialisation de la map
* Ajouté une initialisation de `game.map` dans `Scene_Map::load_map` puisque le pointeur n'est pas initialisé (et la map ne risquait donc pas de charger).
J'ai marqué ce changement "(LEPHE)" parce que je ne sais pas où tu veux le placer. `Game::new_game()` semblait être une bonne idée mais il n'est jamais appelé actuellement, et les maps ne sont pas visibles dans ce fichier.
* Initialisé `Scene_Map::bbox` à `nullptr`, je ne crois pas qu'il y soit tout seul et tu le `free()` immédiatement.
## Test
À ce stade la map se charge mais j'ai un TLB miss (0x08108048, une adresse 0x48 octets après la fin du segment de données) dans `Windmill::draw_post_treatment` lors de l'accès à la VRAM.
* Au passage, j'ai ajouté `-Wl,-Map=map` dans les options d'édition des liens, ce qui génère automatiquement un fichier `build-fx/map` avec l'adresse de chaque fonction et variable, ce qui est utile pour les cas comme celui-ci.
## Correction des accès à la VRAM dans Windmill::draw_post_treatment
Le problème est l'accès ici :
```c
int address = x + y * z_buffer_width + z_buffer_offset;
// ... z_buffer[address] ...
vram[address<<3] |= mask;
```
* `address` est clairement un numéro de pixel, donc c'est `address >> 3` (division par 8) pour indexer le tableau, par `<< 3` (multiplication par 8).
## Test
Avec ça j'obtiens un premier résultat d'image 3D, j'ai la grille au sol, des bouts de texture d'un bâtiment, on progresse !
## Options pour quitter
Je sais pas trop comment tu gères tes changements de scènes, visiblement tu avais une variable globale `int scene` mais elle n'est utilisée nulle part par le programme principal actuellement, donc j'ai commenté les actions qui la modifient.
À la place, j'ai ajouté dans src/main.cpp de quoi quitter avec EXIT et MENU, et j'ai marqué ça "(LEPHE)" puisque je suppose que ce n'est pas le bon endroit. Ça donne d'ailleurs un exemple de l'utilisation de `gint_osmenu()` pour revenir au menu principal comme dans `GetKey()` (attention, quand on revient dans l'add-in une image statique est affichée, ton code ne reprend pas avant qu'une touche soit pressée).
## Note sur l'activation des entrées clavier
Je ne sais plus exactement comment ta bibliothèque input fonctionnait, mais je soupçonne entre `input_trigger()` et `input_press()` que parfois tu ne veux activer ta condition qu'aun moment où la touche descend de la position relâchée à la position enfoncée.
Pour faire ça, tu lis les événements un par un avec `pollevent()` et tu regardes s'il y a un `KEYEV_DOWN` correspondant à ta touche. Lorsqu'il n'y a plus d'événements à lire, `pollevent()` renvoie un événément de type `KEYEV_NONE`.
```c
key_event_t event;
while((event = pollevent()).type != KEYEV_NONE)
{
if(event.type == KEYEV_DOWN && event.key == KEY_F8)
{
/* F8 vient d'être enfoncée */
}
}
```
Pour simplement savoir si une touche est pressée, utilise `keydown()` après avoir lu tous les événements (soit avec `clearevents()` soit avec `pollevent()` dans une boucle comme ci-dessus).
```c
if(keydown(KEY_F8))
{
/* F8 est actuellement pressée (depuis longtemps potentiellement) */
}
```
Attention la fonction `keydown()` n'est pas comme `IsKeyDown()` : elle ne regarde pas l'état absolu de la touche, mais l'état de la touche *tel qu'indiqué par les événements qui ont été lus*. D'où l'importance de lire tous les événements avant pour se synchroniser avec l'état absolu du clavier avant de l'utiliser. Ça peut paraître casse-pieds mais en fait c'est fondamental, ça fait marcher très élégamment les interactions avec `getkey()`.
Comme tu utilises `input_trigger()` dans plusieurs méthodes il n'y a pas de façon évidente de tester les événements (tu ne peux les lire qu'une fois, c'est un flux) donc je n'ai pas touché aux interactions avec EXE, et les F2/F3. Si tu tiens à le gérer dans deux méthodes différentes tu peux recoder ce que tu faisais dans input.c. Mais honnêtement si j'étais toi je mettrais l'analyse des événements dans une seule méthode et ensuite si les événements se produisent tu répartis les actions.
## Activation des contrôles du joueur
Pour le contrôle du joueur (plus facile puisqu'il n'utilise que `input_press()`), j'ai utilisé `keydown()` en remplaçant `input_move()` par une analyse rapide des touches de chiffres 4, 5 et 6. J'ai mis un "(LEPHE)" là-dessus au cas où tu veuilles le déplacer ailleurs. La gestion de la rotation avec les touches fléchées n'a pas demandé de changements particuliers.
J'ai commenté le debug_display("trop loin") parce que je ne suis pas sûr de ce que tu affiches avec cette fonction (pas que des chaînes de caractères apparemment) donc je ne pouvais pas la recoder.
Pour l'instant j'ai mis un `clearevents()` dans src/main.cpp (pour gérer EXIT/MENU) et ça fait marcher le `keydown()` dans le contrôleur du joueur, mais quand tu réarrangeras src/main.cpp n'oublie pas de lire les événements quelque part *avant* d'utiliser `keydown()` (et donc il faut gérer les interactions de type "trigger" avant les appuis continus oui).
## Test
Ça marche, on peut bouger, tourner, sauter, etc. J'ai pas cherché à analyser les performances, mais tu seras peut-être content de savoir qu'il existe une bibliothèque pour gint, libprof [1,2], qui peut mesurer le temps passé dans chaque fonction à la microseconde près.
En tous cas ça a l'air décent niveau perfs, je pense que tout y est ou presque.
## TODO list
Je n'ai pas touché au tri des objets; `qsort()` n'est pas encore implémenté dans notre lib standard. Ça ne devrait pas tarder parce que ce n'est pas bien difficile, mais ce n'est pas encore prêt.
Au passage les en-têtes gint sont protégés pour passer en C++ maintenant. Pas besoin de mettre `extern "C" {}` autour.

View File

@ -26,9 +26,16 @@ int main(void)
dupdate();
scene_map.launch();
while(1)
{
/* (LEPHE) */
clearevents();
if (keydown(KEY_EXIT))
break;
if (keydown(KEY_MENU))
gint_osmenu();
scene_map.draw();
scene_map.update();
}
@ -37,3 +44,18 @@ int main(void)
getkey();
return 1;
}
void debug_pop(char const *fmt, ...)
{
dclear(C_WHITE);
va_list args;
va_start(args, fmt);
char str[256];
vsnprintf(str, 256, fmt, args);
va_end(args);
dtext(1, 1, C_BLACK, str);
dupdate();
getkey();
}

View File

@ -1,8 +1,8 @@
extern "C"
{
#include <gint/display.h>
#include <gint/keyboard.h>
}
#include <gint/display.h>
#include <gint/keyboard.h>
#include <gint/gint.h>
#include <stdio.h>
#include "game.hpp"
#include "windmill.hpp"

View File

@ -1,5 +1,6 @@
#include "scene_map.hpp"
void debug_pop(char const *fmt, ...);
extern Game game;
extern Windmill windmill;
@ -25,6 +26,8 @@ Scene_Map::Scene_Map()
vue_horloge = false;
vue_panneau_moulin = false;
parle_avec_forgeron = false;
bbox = nullptr;
}
//----------------------------------------------------------------------------------------------------
@ -44,6 +47,9 @@ void Scene_Map::launch()
void Scene_Map::load_map()
{
// (LEPHE)
game.map = &map_village;
// mise a jour de la map pour la scene
windmill.load_map(game.map);
@ -52,7 +58,7 @@ void Scene_Map::load_map()
free(bbox);
//bbox = new Bbox3D[game.map->list_object_length];
bbox = (Bbox3D*) malloc(game.map->list_object_length * sizeof(Bbox3D));
//if (bbox == NULL) debug_pop("ERROR ptr bbox NULL");
if (bbox == NULL) debug_pop("bbox NULL: %d", game.map->list_object_length);
memset(bbox, 0, game.map->list_object_length * sizeof(Bbox3D));
for (int i=0; i<game.map->list_object_length; i++)
@ -251,14 +257,17 @@ void Scene_Map::animation()
void Scene_Map::player_move()
{
/*///////////// METTRE DANS PLAYER ET RETURN LE FLAG
///////////// METTRE DANS PLAYER ET RETURN LE FLAG
float new_x = game.player.x;
float new_y = game.player.y;
float new_z = game.player.z;
// deplacement
float front, side;
input_move(&front, &side);
/* (LEPHE) */
// input_move(&front, &side);
front = 2 * (keydown(KEY_5) != 0);
side = 2 * ((keydown(KEY_6) != 0) - (keydown(KEY_4) != 0));
// si des touches sont pressees
if (side != 0 or front != 0)
@ -283,7 +292,7 @@ void Scene_Map::player_move()
}
// saut
if (input_press(K_8) and not game.player.jumping)
if (keydown(KEY_8) and not game.player.jumping)
{
game.player.jump_speed = game.player.jump_speed_max;
game.player.jumping = true;
@ -305,25 +314,24 @@ void Scene_Map::player_move()
// hors terrain
if (flag.type == COLLISION_BOUND)
{
debug_display("trop loin", 2);
//debug_display("trop loin", 2);
}
game.player.x = new_x;
game.player.y = new_y;
game.player.z = new_z;
*/
}
void Scene_Map::player_turn()
{/*
{
float yaw = 0;
float pitch = 0;
if (input_press(K_RIGHT)) yaw = -1;
if (input_press(K_LEFT)) yaw = 1;
if (input_press(K_DOWN)) pitch = -0.5;
if (input_press(K_UP)) pitch = 0.5;
if (keydown(KEY_RIGHT)) yaw = -1;
if (keydown(KEY_LEFT)) yaw = 1;
if (keydown(KEY_DOWN)) pitch = -0.5;
if (keydown(KEY_UP)) pitch = 0.5;
if (yaw != 0 or pitch != 0)
{
@ -346,7 +354,6 @@ void Scene_Map::player_turn()
game.player.yaw = yaw;
game.player.pitch = pitch;
*/
}
@ -736,7 +743,7 @@ Object* Scene_Map::object(int id)
{
if (id < 0 or id >= game.map->list_object_length)
{
//debug_pop("ERROR id out of range");
debug_pop("ERROR id out of range");
return NULL;
}
return game.map->object[id];
@ -753,7 +760,7 @@ int Scene_Map::id(Object* object)
return i;
}
}
//debug_pop("ERROR object not found");
debug_pop("ERROR object not found");
return -1;
}

View File

@ -13,6 +13,8 @@
#include "windmill.hpp"
void debug_pop(char const *fmt, ...);
//const Texture tex_black = {};
//const Texture tex_white = {};
extern Texture tex_white;
@ -58,7 +60,7 @@ void Windmill::set_viewport(int viewport_x1, int viewport_y1, int viewport_x2, i
z_buffer_size = (viewport.x2 - viewport.x1) * (viewport.y2 - viewport.y1);
z_buffer = (unsigned short*) calloc(z_buffer_size, 2);
//if (z_buffer == NULL) debug_pop("ERROR ptr z_buffer NULL");
if (z_buffer == NULL) debug_pop("ERROR ptr z_buffer NULL");
z_buffer_width = viewport.x2 - viewport.x1;
z_buffer_offset = - viewport.x1 - viewport.y1 * DWIDTH;
clear_z_buffer();
@ -85,7 +87,7 @@ void Windmill::load_map(Map* _map)
free(object);
//object = new Object* [list_object_length];
object = (Object**) malloc(list_object_length * sizeof(Object));
//if (object == NULL) debug_pop("ERROR ptr object NULL");
if (object == NULL) debug_pop("ERROR ptr object NULL");
for (int i = 0; i<list_object_length; i++)
{
object[i] = map->object[i];
@ -381,7 +383,7 @@ void Windmill::draw_post_treatment()
if (right < MAX_DEPTH_Z_BUFFER)
{
mask = 128 >> ((x+1) & 7);
vram[address<<3] |= mask;
vram[address>>3] |= mask;
//ML_pixel(x+1, y, ML_BLACK);
}