Attraverso l'SDK è possibile creare qualunque tipo di modulo (libreria), spaziando da una semplice libreria di funzioni fino a engines audio, video, 2D, 3D, etc...
COMPILATORI E LINGUAGGI PER LA CREAZIONE DI MODULI
I moduli di Gemix possono essere programmati in linguaggi come C, C++, Assembly.
E' possibile usare qualunque tipo di compilatore C/C++ esistente per i linguaggi sopracitati, tuttavia si raccomandano i seguenti:
Windows
- Visual Studio 2008 Express Edition o superiore (o versione superiore a pagamento).
- Intel.
- MinGW (si raccomanda usare Qt come ambiente di sviluppo).
- LLVM.
- Intel, LLVM, GCC.
- Intel, LLVM, GCC.
NOMENCLATURA DEI MODULI
Per creare un modulo non ufficiale è possibile utilizzare qualunque tipo di nomenclatura (eccetto quella ufficiale mensionata in basso), tuttavia si raccomanda si seguire il seguente LAYOUT di stile:
GMXEXT_mod_<name>
- <name> - Rappresenta il nome del modulo (libreria) da creare.
Esempio:
GMXEXT_mod_mymath
NOTA:
- Skygem Software si riserva il diritto di utilizzare la nomenclatura GMX_mod_<name> (con prefisso GMX_) per i moduli ufficiali, e qualunque tentativo di utilizzarlo para moduli non ufficiali verrà rifiutato.
STRUTTURA DEI MODULI
Esiste un LAYOUT da seguire per l'implementazione di un modulo (libreria) per Gemix:
- Creare files sorgenti con estensione .CPP (per le implementazioni) e .H/HPP (si raccomanda .H per gli headers).
- Il file principale (può essere chiamato main.cpp o <name>.cpp, dove <name> è lo stesso nome del modulo) deve contenere la seguente definizione:
1 - DEFINIZIONE DEL GMXRE
Il GMXRE (Gemix Runtime Environment) è un puntatore alla struttura gestionata dal core per l'esecuzione dei programmi.
Mediante questa struttura è possibile gestire la maggior parte delle operazioni, come ottenere il numero di parametri di una funzione, chiamare entrypoints, etc...
NOTA:
- Non è necessario creare manualmente questo puntatore, verrà creato automaticamente nella definizione del GMXDEFINE_LIBRARY_EXPORTS (vedere in basso).
2 - USO E DEFINIZIONE DI GMXDEFINE_LIBRARY_DEPENDENCIES
Certe volte una libreria può necessitare di dati o funzioni di un'altra (dipendenze) per funzionare o compilare.
La macro GMXDEFINE_LIBRARY_DEPENDENCIES permette di definire le dipendenze del modulo che si sta creando.
La forma è la seguente:
GMXDEFINE_LIBRARY_DEPENDENCIES(GMX_module_name, ...);
- GMX_module_name - E' il nome del modulo che si sta creando.
- ... - E' una lista di una o più stringhe, che include i nomi dei moduli.
Esempio:
- GMXDEFINE_LIBRARY_DEPENDENCIES(GMXEXT_mod_sdkexample,
- "GMX_mod_math"
- );
Questo esempio indica che il modulo necessita (dipende) da alcuni dati del modulo "math".
3 - USO E DEFINIZIONE DI GMXDEFINE_LIBRARY_EXPORTS
La macro GMXDEFINE_LIBRARY_EXPORTS definisce la funzione principale che verrà chiamata dal compilatore o core di Gemix durante il caricamento del modulo.
La forma è la seguente:
GMXDEFINE_LIBRARY_EXPORTS(GMX_module_name, ...);
- GMX_module_name> - Rappresenta il nome del modulo (per esempio "mymath").
- ... - E' una lista di macro per la definizione degli elementi del modulo (costanti, variabili, funzioni, etc...).
Esempio:
- GMXDEFINE_LIBRARY_EXPORTS(GMXEXT_mod_mymodule,
- /* CONSTS */
- ...
- /* TYPES */
- ...
- /* GLOBALS */
- ...
- /* LOCALS */
- ...
- /* FUNCTIONS */
- ...
- /* ENTRYPOINTS */
- ...
- /* PRIORITY */
- ...
- );
NOTA:
- Si raccomanda di utilizzare il prefisso GMXEXE_mod_ per i moduli.
Nella funzione è possibile utilizzare le seguenti macro:
- GMXDEFINE_CONSTS_INT
- GMXDEFINE_CONSTS_FLOAT
- GMXDEFINE_CONSTS_DOUBLE
- GMXDEFINE_TYPES
- GMXDEFINE_GLOBALS
- GMXDEFINE_LOCALS
- GMXDEFINE_FUNCTIONS
- GMXDEFINE_ENTRYPOINTS
- GMXDEFINE_PRIORITY
NOTA:
- Queste macro possono essere utilizzate solo in GMXDEFINE_LIBRARY_EXPORTS, il tentativo di usarle fuori dalla funzione produce un errore di compilazione.
- E' possibile utilizzare la maggior parte delle macro in qualunque ordine, tuttavia si raccomanda il seguente LAYOUT:
- CONSTS
- TYPES
- GLOBALS
- LOCALS
- FUNCTIONS
- ENTRYPOINTS
- PRIORITY
- Alcune macro (per esempio GLOBAL/LOCAL/FUNCTION), potrebbero necessitare di alcune definizioni precedenti, per esempio se utilizzano tipi user-def (TYPE).
3.1 - USO E DEFINIZIONE DI GMXDEFINE_LIBRARY_EXPORTS - DEFINIRE COSTANTI
Per poter definire nuove costanti predefinite nel linguaggio si devono usare macro della seguente forma:
DEFINE_CONSTS_<TYPE>(...);
- <TYPE> - Rappresenta il tipo di costante (INT, FLOAT o DOUBLE).
- ... - Rappresenta una lista formata dal nome della costante (es: my_pi) ed il valore della costante (es: 3.141592, 100, etc...).
Esempio:
- GMXDEFINE_LIBRARY_EXPORTS(GMXEXT_mod_mymodule,
- /* CONSTS */
- GMXDEFINE_CONSTS_INT(
- "my_speed", 100
- );
- GMXDEFINE_CONSTS_FLOAT(
- "my_gravity", 127.53f
- );
- GMXDEFINE_CONSTS_DOUBLE(
- "my_pi", 3.141592
- );
- ...
- );
La definizione precedente crea 3 nuove costanti predefinite nel linguaggio chiamate my_speed, my_gravity e my_pi ed è equivalente alla seguente definizione in Gemix:
- const
- my_speed = 100;
- my_gravity = 127.53f;
- my_pi = 3.141592;
3.2 - USO E DEFINIZIONE DI GMXDEFINE_LIBRARY_EXPORTS - DEFINIRE TYPE'S
Per poter definire nuovi type's predefiniti nel linguaggio si deve usare la macro GMXDEFINE_TYPES nella seguente forma:
GMXDEFINE_TYPES(string_types_def);
- "string_types_def" - Rappresenta una stringa con le definizioni di types, utilizzando la stessa sintassi del linguaggio.
Esempio:
- GMXDEFINE_LIBRARY_EXPORTS(GMXEXT_mod_mymodule,
- /* CONSTS */
- ...
- /* TYPES */
- GMXDEFINE_TYPES(
- /* shape */
- "type shape "
- "int type;"
- "string name[100];"
- "float radius;"
- "float x;"
- "float y;"
- "shape *next;"
- "end"
- /* triangle */
- "type triangle "
- "shape property;"
- "int num_vertices;"
- "int vertices[3];"
- "end"
- );
- );
La definizione precedente crea 2 nuovi tipi predefiniti nel linguaggio chiamati shape e triangle ed è equivalente alla seguente definizione in Gemix:
- typedef
- type shape
- int type;
- string name[99]; // (100 se si utilizza l'opzione _use_cstyle_matrix o _use_cstyle);
- float radius;
- float x;
- float y;
- shape *next;
- end
- type triangle
- shape property;
- int num_vertices;
- int vertices[2]; // (3 se si utilizza l'opzione _use_cstyle_matrix o _use_cstyle);
- end
NOTA:
- Come nel linguaggio, è necessario definire prima un type se questo verrà utilizzato come tipo per campi di un'altro type.
- Un type può contenere un puntatore del suo stesso tipo, pero no una varibile/array.
- Un type deve contenere almeno un campo affinchè la definizione sia valida.
- Se un type contiene array o string di N numero di caratteri, la grandezza tra [ ] deve porsi come se si utilizzasse l'opzione CSTYLE_MATRIX del linguaggio.
3.3 - USO E DEFINIZIONE DI GMXDEFINE_LIBRARY_EXPORTS - DEFINIRE VARIABILI, ARRAY E STRUCTS
Per poter definire nuovi dati (variabili, array, structs) predefiniti nel linguaggio si devono utilizzare macro nella seguente forma:
GMXDEFINE_GLOBALS(strings_globals_def);
GMXDEFINE_LOCALS(strings_locals_def);
- "string_globals/locals_def" - Rappresenta una stringa con le definizioni dei dati GLOBAL o LOCAL, utilizzando la stessa sintassi del linguaggio.
Esempio:
- GMXDEFINE_LIBRARY_EXPORTS(GMXEXT_mod_mymodule,
- /* CONSTS */
- ...
- /* TYPES */
- ...
- /* GLOBALS */
- GMXDEFINE_GLOBALS(
- /* VARIABLES */
- "int my_speed = 100;"
- "float my_gravity = 127.53f;"
- "double my_pi = 3.141592;"
- "shape shape_;"
- "float my_gravites = 100;"
- /* ARRAYS */
- "int my_speeds[100];"
- "shape shapes[100];"
- /* STRUCTS */
- "struct shape_st[10]"
- "int type;"
- "string name[100];"
- "float radius;"
- "float x;"
- "float y;"
- "end"
- );
- );
La definizione precedente crea 4 nuove variabili predefinite GLOBAL chiamate my_speed, my_gravity, my_pi e shape_, 3 nuovi array predefiniti GLOBAL chiamati my_speeds, my_gravites e shapes di 100 registri ciascuno ed 1 nuova struttura predefinita di 10 registri nel linguaggio chiamata shape_st ed è equivalente alla seguente definizione in Gemix:
- global
- int my_speed = 100;
- float my_gravity = 127.53f;
- double my_pi = 3.141592;
- shape shape_;
- int my_speeds[99]; // (100 se si utilizza l'opzione _use_cstyle_matrix o _use_cstyle)
- float my_gravites[99]; // (100 se si utilizza l'opzione _use_cstyle_matrix o _use_cstyle)
- shape shapes[99]; // (100 se si utilizza l'opzione _use_cstyle_matrix o _use_cstyle)
- struct shape_st[9]; // (10 se si utilizza l'opzione _use_cstyle_matrix o _use_cstyle)
- int type;
- string name[99]; // (100 se si utilizza l'opzione _use_cstyle_matrix o _use_cstyle)
- float radius;
- float x;
- float y;
- end
NOTA:
- Si devono utilizzare le stesse regole del linguaggio nella dichiarazione di variabili, array e structs, con l'unica eccezione che la grandezza di array, struct o caratteri di stringhe tra [ ] deve porsi come se si stesse utilizzando l'opzione CSTYLE_MATRIX del linguaggio.
3.4 - USO E DEFINIZIONE DI GMXDEFINE_LIBRARY_EXPORTS - DEFINIRE FUNZIONI
Per poter definire nuove funzioni predefinite nel linguaggio si deve utilizzare la macro GMXDEFINE_FUNCTIONS nella seguente forma:
GMXDEFINE_FUNCTIONS(...);
- ... - Rappresenta una lista di stringhe e valori separati da virgole del tipo:
- "name([params]]" - Rappresenta il nome della funzione in Gemix e i suoi eventuali parametri (es: my_max()), dove:
- "params" - Rappresenta la signature della funzione (tipo e numero di parametri) indicato mediante pattern utilizzando la seguente nomenclatura separata da ",":
- SB = Signed Byte (SBYTE in Gemix)
- SBP = Signed Byte Pointer (SBYTE * in Gemix)
- SW = Signed Word (SHORT in Gemix)
- SWP = Signed Word Pointer (SHORT * in Gemix)
- B = Unsigned Byte (BYTE in Gemix)
- BP = Unsigned Byte Pointer (BYTE * in Gemix)
- W = Unsigned Word (WORD in Gemix)
- WP = Unsigned Word Pointer (WORD * in Gemix)
- I = Signed Int (INT in Gemix)
- IP = Signed Int Pointer (INT * in Gemix)
- DW = Unsigned Int (DWORD in Gemix)
- DWP = Unsigned Int Pointer (DWORD * in Gemix)
- F = Float (FLOAT in Gemix)
- FP = Float Pointer (FLOAT * in Gemix)
- D = Double (DOUBLE in Gemix)
- DP = Double Pointer (DOUBLE * in Gemix)
- S = String (STRING in Gemix)
- SP = String Pointer (STRING * in Gemix)
- ST = Struct (STRUCT in Gemix)
- STP = Struct Pointer (STRUCT * in Gemix)
- T(name) = Type (TYPE in Gemix)
- TP(name) = Type Pointer (TYPE * in Gemix)
- FO = Function Object (FOBJECT in Gemix)
- FOP = Function Object Pointer (FOBJECT * in Gemix)
- V = Void (Qualunque <tipo> in Gemix)
- VP = Void Pointer (Qualunque <tipo> * in Gemix)
- "params" - Rappresenta la signature della funzione (tipo e numero di parametri) indicato mediante pattern utilizzando la seguente nomenclatura separata da ",":
- "rettype" - Rappresenta il tipo di ritorno della funzione, indicato mediante la seguente nomenclatura:
- SB = Signed Byte (SBYTE in Gemix)
- SBP = Signed Byte Pointer (SBYTE * in Gemix)
- SW = Signed Word (SHORT in Gemix)
- SWP = Signed Word Pointer (SHORT * in Gemix)
- B = Unsigned Byte (BYTE in Gemix)
- BP = Unsigned Byte Pointer (BYTE * in Gemix)
- W = Unsigned Word (WORD in Gemix)
- WP = Unsigned Word Pointer (WORD * in Gemix)
- I = Signed Int (INT in Gemix)
- IP = Signed Int Pointer (INT * in Gemix)
- DW = Unsigned Int (DWORD in Gemix)
- DWP = Unsigned Int Pointer (DWORD * in Gemix)
- F = Float (FLOAT in Gemix)
- FP = Float Pointer (FLOAT * in Gemix)
- D = Double (DOUBLE in Gemix)
- DP = Double Pointer (DOUBLE * in Gemix)
- S = String (STRING in Gemix)
- SP = String Pointer (STRING * in Gemix)
- ST = Struct (STRUCT in Gemix)
- STP = Struct Pointer (STRUCT * in Gemix)
- T(name) = Type (TYPE in Gemix)
- TP(name) = Type Pointer (TYPE * in Gemix)
- VP = Void Pointer (Qualunque <tipo> * in Gemix)
- is_timing - Indica se la funzione richiede tempo per la sua esecuzione o no (si utilizza per il controllo PROCESS_TIME).
- hfunction - Puntatore alla funzione reale che verrà chiamata al invocare la funzione in Gemix.
- "name([params]]" - Rappresenta il nome della funzione in Gemix e i suoi eventuali parametri (es: my_max()), dove:
Esempio:
- GMXDEFINE_LIBRARY_EXPORTS(GMXEXT_mod_mymodule,
- /* CONSTS */
- ...
- /* TYPES */
- ...
- /* GLOBALS */
- ...
- /* LOCALS */
- ...
- /* FUNCTIONS */
- GMXDEFINE_FUNCTIONS(
- "image_load(S)", "I", 1, GMXEXT_image_load,
- "my_max(I,I)", "I", 0, GMXEXT_my_max,
- "set_rotation(I,F=1.0f,F=1.0f,F=1.0f,F=my_pi)", "I", 0, GMXEXT_set_rotation
- );
- ...
- );
La definzione precedente crea 2 nuove funzioni predefinite nel linguaggio chiamate image_load, my_max e set_rotation:
- La prima funzione restituisce un tipo INT, accetta come parametro 1 tipo STRING, il suo numero di parametri è 1, richiede tempo per la sua esecuzione e quando è invocata, chiama la funzione reale GMXEXT_image_load.
- La seconda funzione restituisce un tipo INT, accetta come parametri 2 tipi INT, il suo numero di parametri è 2, non richiede tempo per la sua esecuzione e quando viene invocata, chiama la funzione reale GMXEXT_my_max. In questa funzione è possibile notare come i tipi dei parametri vengono separati mediante il simbolo ",".
- La terza funzione restituisce un tipo INT, accetta come parametri 1 tipo INT e 4 tipi FLOAT, il suo numero di parametri è 5, non richiede tempo per la sua esecuzione e quando viene invocata, chiama la funzione reale GMXEXT_set_rotation. In questa funzione è possibile notare come i tipi dei parametri vengono separati mediante il simbolo "," e che gli ultimi 4 parametri hanno argomenti di default.
- "rettype" può contenere solo un tipo e non può contenere, iniziare o terminare con il simbolo ",", altrimenti si avrà un errore nel caricamento del modulo.
- Una signature ("params"), può essere vuota (()) indicando che la funzione non ha parametri.
- "params" non può iniziare o terminare con il simbolo "," e non può avere spazi tra i simboli dei parametri, altrimenti si avrà un errore nel caricamento del modulo.
- Se un parametro ha un argomento di default, anche i successivi devono averlo, altrimenti si avrà un errore nel caricamento del modulo.
- Se si utilizza un identificatore non valido come valore di argomento di default (il nome non esiste o non corrisponde ad una costante valida), si avrà un errore nel caricamento del modulo.
Dopo aver definito la funzione del linguaggio, si deve definire la funzione reale che verrà chiamata quando verrà invocata da Gemix la funzione del linguaggio stessa.
La funzione reale deve avere la seguente forma:
GMXvoid GMXEXT_<name>() {
[<Type> variable = Get<Type>Param();]
...
Ret<Type>Value(return_value);
}
- <name> - Rappresenta il nome della funzione reale (es: my_max).
- <Type> - Rappresenta il tipo di parametro/ritorno della funzióne (INT, DOUBLE, etc...).
NOTA:
- Si raccomanda di utilizzare il prefisso GMXEXT_ per indicare il nome della funzione ed evitare possibili errori di duplicazione dei simboli.
- Il <Type> deve coincidere con il tipo indicato in "rettype" nella definizione della funzione Gemix o un tipo eventualmente compatibile, viceversa potrebbe verificarsi un crash del programma durante l'esecuzione della funzione.
3.5 - USO E DEFINIZION DI GMXDEFINE_LIBRARY_EXPORTS - DEFINIRE ENTRYPOINTS
Un entrypoint è una funzione callback chiamata dal core o da qualunque altra funzione in un determinato momento per eseguire qualche operazione specifica.
Per poter definire un entrypoint si deve utilizzare la macro GMXDEFINE_ENTRYPOINTS nella seguente forma:
GMXDEFINE_ENTRYPOINTS(...);
- ... - Rappresenta una lista con il tipo di entrypoint da definire (init, frame, etc...) e la funzione-callback reale chiamata quando viene invocato l'entrypoint.
- Attualmente è possibile definire i seguenti entrypoints:
- GMX_init - Invocato quando la libreria viene caricata. Utile per inizializzare dati, allocare risorse per la libreria, etc...
- GMX_frame - Invocato ogni frame. Utile per aggiornare/eseguire dati particolari necessari ad ogni frame.
- GMX_update - Invocato in momenti particolari. Utile quando qualche funzione deve aggiornare parti cruciali di tutto il sistema.
- GMX_release - Invocato prima della finalizzazione del programma. Utile per de-allocare risorse allocare da GMX_init.
- La callback_function che rappresenta la funzione reale invocata deve avere la seguente forma:
void GMXEXT_mod_<name>_<entrypoint>() {
...
}- <name> - Rappresenta il nome del modulo.
- <entrypoint> - Rappresenta il nome entrypoint della funzione callback (es: init, frame, etc...).
- Attualmente è possibile definire i seguenti entrypoints:
Esempio:
- GMXDEFINE_LIBRARY_EXPORTS(GMXEXT_mod_mymodule,
- /* CONSTS */
- ...
- /* TYPES */
- ...
- /* GLOBALS */
- ...
- /* LOCALS */
- ...
- /* FUNCTIONS */
- ...
- /* ENTRYPOINTS */
- GMXDEFINE_ENTRYPOINTS(
- GMX_init, GMXEXT_mod_mymodule_init,
- GMX_frame, GMXEXT_mod_mymodule_frame,
- GMX_release, GMXEXT_mod_mymodule_release
- );
- ...
- }
- void GMXEXT_mod_mymodule_init() {
- // alloca un array di risorse quando la libreria viene caricata
- ptr = (int)malloc(10000 * sizeof(int));
- ...
- }
- void GMXEXT_mod_mymodule_frame() {
- // aggiorna casualmente alguni elementi dell'array di risorse ad ogni frame
- ptr[0] = rand() % 1000;
- ptr[1] = rand() % 100;
- ptr[2] = rand() % 300;
- }
- void GMXEXT_mod_mymodule_release() {
- // de-alloca l'array di risorse prima di scaricare la libreria
- if(ptr) {
- free(ptr);
- }
- }
NOTA:
- Si raccomanda di utilizzare il prefisso GMXEXT_mod_<name> per indicare il nome della funzione ed evitare possibili errori di duplicazione dei simboli.
- Gli entrypoints sono opzionali, è possibile che alcune librerie non necessitino alcuno, tutti o alcuni.
3.6 - USO E DEFINIZIONE DI GMXDEFINE_LIBRARY_EXPORTS - IMPOSTARE LA PRIORITA' DI UNA LIBRERIA
Per poter impostare la priorità di esecuzione di una libreria si deve utilizzare la macro GMXDEFINE_PRIORITY nella seguente forma:
GMXDEFINE_PRIORITY(value)
- value - Rappresenta il valore di priorità di esecuzione della libreria, per default il valore è 0.
Quanto più alto è il valore, prima verrà eseguita la libreria.
Esempio:
- GMXDEFINE_LIBRARY_EXPORTS(GMXEXT_mod_mymodule,
- /* CONSTS */
- ...
- /* TYPES */
- ...
- /* GLOBALS */
- ...
- /* LOCALS */
- ...
- /* FUNCTIONS */
- ...
- /* ENTRYPOINTS */
- ...
- /* PRIORITY */
- GMXDEFINE_PRIORITY(100);
- );
NOTA:
- La priorità incide nell'ordine di caricamento ed esecuzione degli entrypoints, per l'entrypoint frame tuttavia l'ordine incide al contrario, quanto più alta è la priorità, più tardi verra eseguito l'entrypoint.
Di seguito viene fornito un esempio di un mini modulo, questo definisce:
- Alcune costanti INT e FLOAT.
- Un nuovo tipo di dato (TYPE).
- Una nuova variabile GLOBAL.
- 3 nuove funzioni (due con 3 overloads ciascuno).
Il modulo è stato compilato con Visual Studio 2010 Express edition, tuttavia è possibile utilizzare il codice e compilare con qualunque altro compilatore indicato all'inizio della DOC.
SDK Example