GLFW04 Interaksi - auziasfarian/CG-IPB GitHub Wiki
Pada modul kali ini, kita akan melihat beberapa sarana interaksi yang mungkin dibuat dengan GLFW.
Catatan: Berbeda dengan modul sebelumnya, kali ini Anda dipaksa untuk menggunakan object-oriented programming pada C++. Hal ini untuk memudahkan Anda saat membuat aplikasi interaktif yang lebih kompleks.
Pada sisi komputer, segala sesuatu yang dilakukan oleh pengguna saat berinteraksi dengan komputer disebut sebagai event. Ada banyak event yang mungkin terjadi, seperti tombol yang ditekan pada keyboard, tombol yang ditekan pada mouse, bahkan perubahan ukuran window yang dilakukan oleh pengguna. Agar dapat berjalan secara interaktif, suatu aplikasi harus merespons event yang terjadi melalui suatu fungsi yang disebut sebagai fungsi callback. Lazimnya, ada satu fungsi callbacks yang bertanggung jawab untuk merespons setiap jenis event yang terjadi.
Sebagai sebuah windowing API yang mendukung OpenGL, GLFW memiliki beberapa jenis callbacks bawaan yang dapat digunakan oleh Anda. Pada dasarnya, yang perlu Anda lakukan adalah membuat implementasi dari fungsi callbacks dan mendaftarkannya di dalam program. Setelah itu, setiap kali suatu event terjadi, program akan memanggil fungsi callbacks yang sesuai. Apabila Anda tidak mendaftarkan fungsi _callbacks tersebut, event tersebut akan diabaikan oleh program.
Ada dua jenis callback yang akan kita coba pada sesi kali ini. Jenis pertama adalah yang berhubungan dengan alat masukan, sedangkan jenis kedua adalah yang berhubungan dengan window.
Jenis pertama terbagi menjadi:
-
glfwSetKeyCallback
: tombol pada keyboard. -
glfwSetCharCallback
: text stream pada keyboard. -
glfwSetCursorPosCallback
: perubahan posisi kursor pada layar. -
glfwSetCursorEnterCallback
: kursor keluar atau masuk dari window. -
glfwSetMouseButtonCallback
: tombol yang ditekan pada mouse. -
glfwSetScrollCallback
: tombol scroll pada mouse. -
glfwSetJoystickCallback
: mengecek apakah ada joystick yang terhubung.
Jenis kedua terbagi menjadi:
-
glfwSetWindowCloseCallback
: jendela ditutup. -
glfwSetWindowSizeCallback
: ukuran jendela diubah. -
glfwSetFramebufferSizeCallback
: ukuran framebuffer diubah. -
glfwSetWindowContentScaleCallback
: rasio jendela diubah. -
glfwSetWindowPosCallback
: posisi koordinat jendela diubah. -
glfwSetWindowIconifyCallback
: jendela di-minimize. -
glfwSetWindowMaximizeCallback
: jendela di-maximize. -
glfwSetWindowFocusCallback
: jendela aktif atau tidak aktif.
Sekarang, kita akan melihat bagaimana cara merespons event dengan menggunakan callback. Sebagai demo, kita akan menggunakan sebuah class bernama Rectangle
yang dapat digunakan untuk menggambar sebuah kotak di layar.
#include <GLFW/glfw3.h>
#include <iostream>
#include <list>
#include <iterator>
using namespace std;
/*
* CLASSES DECLARATION
*/
// Rectangle class
class Rectangle{
public:
double x=200, y=200;
double prevY = 0;
int red = 255, green = 255, blue = 255;
int size = 20;
void display()
{
glBegin(GL_POLYGON);
glColor3ub(red, green, blue);
glVertex2f(-size + x, size + y);
glVertex2f( size + x, size + y);
glVertex2f( size + x, -size + y);
glVertex2f(-size + x, -size + y);
glEnd();
}
GLboolean isRectangleSelected(double xpos, double ypos){return GL_TRUE;}
void doIfSelected(double xpos, double ypos){}
void doIfKeyPressed(int key, int action){}
void doIfClicked(int button, int action, double xpos, double ypos){}
};
Rectangle rect;
Pada bagian selanjutnya, kita akan mendefinisikan prototipe dari fungsi callbacks yang akan kita buat. Pada saat ini, selain error callback
ketiga fungsi lainnya masih kita kosongkan.
/*
* CALLBACKS FUNCTIONS
*/
static void error_callback(int error, const char* description) {
//fputs(description);
}
// Callback untuk merespons penekanan kunci pada keyboard
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){}
// Callback untuk merespons perubahan posisi kursor
static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos){}
// Callback untuk merespons penekanan tombol pada mouse
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods){}
Pada bagian terakhir, kita menambahkan fungsi main
untuk menjalankan program.
/*
* MAIN FUNCTION
*/
int main(void) {
/* Creating Windows */
GLFWwindow* window;
glfwSetErrorCallback(error_callback);
if (!glfwInit());
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(400, 400, "Interaction", NULL, NULL);
if (!window){
glfwTerminate();
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
/* Registering Callbacks */
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
/* MAIN LOOP */
while (!glfwWindowShouldClose(window)){
/* Preparing Frame Creation */
float ratio;
int width, height;
glfwGetFramebufferSize(window, &width, &height);
ratio = width / (float) height;
glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 400, 400 , 0 , 1.f, -1.f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* Object Drawing*/
rect.display();
/* Handling Frame Changes */
glfwSwapBuffers(window);
glfwPollEvents();
}
/* Destroying Windows */
glfwDestroyWindow(window);
glfwTerminate();
//exit(EXIT_SUCCESS);
}
Secara umum, struktur fungsi main
tersebut masih sama dengan program kita pada pekan sebelumnya. Hanya saja, sekarang kita memiliki tambahan tiga buah fungsi, yaitu glfwSetKeyCallback()
, glfwSetCursorPosCallback()
, dan glfwSetMouseButtonCallback()
, sebelum loop utama program dijalankan. Ketiga fungsi ini digunakan untuk mendaftarkan fungsi callback yang telah kita siapkan sebelumnya.
Sekarang, jalankan program tersebut, dan apabila berhasil, sebuah kotak putih akan muncul pada bagian tengah layar.
Sekarang, kita akan mencoba membuat fungsi callbacks untuk merespons perubahan posisi kursor pada layar. Hal yang ingin kita lakukan adalah:
- Ketika kursor berada di dalam kotak, warna kotak akan berubah menjadi hijau.
- Ketika kursor berada di luar kotak, warna kotak akan berubah menjadi putih.
Agar hal ini dapat dilakukan, kita akan membuat implementasi dari tiga fungsi, yaitu
isRectangleSelected
untuk mendeteksi apakah kursor berada di dalam kotak,doIfSelected
untuk mengubah warna kotak ketika kotak tersebut dipilih, sertacursor_position_callback
.
Pada fungsi doIfSelected
, kita harus melakukan mengecek posisi kursor terhadap tepi luar kotak yang digambar. Hal ini dapat dilakukan dengan mudah dengan melakukan pengecekan sederhana terhadap sisi kiri, kanan, atas, dan bawah dari kotak. Implementasi kode dari hal tersebut adalah sebagai berikut:
GLboolean isRectangleSelected(double xpos, double ypos){
double left = x - size;
double right = x + size;
double top = y + size;
double bottom = y - size;
if((xpos > left) && (xpos < right) && (ypos < top) && (ypos > bottom))
return GL_TRUE;
else
return GL_FALSE;
}
Fungsi ini kemudian kita gunakan pada fungsi doIfSelected
.
void doIfSelected(double xpos, double ypos){
if(isRectangleSelected(xpos, ypos) == GL_TRUE)
red = blue = 0;
else
red = blue = 255;
}
Setelah kita memodifikasi fungsi pada class Rectangle
tersebut, tugas berikutnya adalah memastikan fungsi tersebut dipanggil ketika posisi mouse berubah. Caranya adalah dengan menambahkan baris berikut pada implementasi fungsi cursor_position_callback
:
static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos){
rect.doIfSelected(xpos, ypos);
}
Setiap kali event perubahan posisi kursor terjadi, fungsi tersebut akan mendapatkan tiga informasi, yaitu pointer ke window tempat kursor berada serta kordinat x dan y kursor pada koordinat window. Ingat bahwa titik asal (0,0) pada koordinat window berada pada sisi kiri atas. Informasi koordinat tersebut kemudian kita teruskan ke kotak yang kita gambar untuk selanjutkan dicek posisi terhadap tepian kotak.
Setelah selesai, Anda dapat menjalankan program tersebut dan melihat hasilnya.
Berikutnya, kita akan mencoba merespons penekanan tombol kiri dan kanan pada mouse. Aksi yang dilakukan sederhana, yaitu posisi kotak akan berpindah ke koordinat tempat kursor diklik.
Pertama, kita buat implementasi dari fungsi doIfClicked
. Di sini kita akan melakukan pengecekan terhadap tombol (button
) dan aksi (action
) yang dilakukan oleh pengguna. Pada kali ini, kita akan mengecek apakah pengguna menekan (GLFW_PRESS
) tombol kiri mouse (GLFW_MOUSE_BUTTON_LEFT
). Apabila kedua kondisi tersebut terpenuhi, posisi kordinat dari kotak akan berpindah ke posisi tempat kursor berada saat event terjadi (xpos
dan ypos
).
void doIfClicked(int button, int action, double xpos, double ypos){
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS){
x = xpos;
y = ypos;
}
}
Kemudian, kita panggil fungsi tersebut pada mouse_button_callback
. Jalankan program, dan coba untuk mengklik tombol kanan mouse di berbagai tempat untuk memindahkan koordinat kotak tersebut.
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods){
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
rect.doIfClicked(button, action, xpos, ypos);
}
Terakhir adalah merespons penekanan tombol pada keyboard. Kita akan menggerakkan kotak yang kita punya dengan menekan tombol panah pada keyboard. Untuk dapat melakukan hal tersebut, kita modifikasi fungsi doIfKeyPressed
menjadi seperti berikut:
void doIfKeyPressed(int key, int action){
if (key == GLFW_KEY_UP && action == GLFW_PRESS)
y-=10;
if (key == GLFW_KEY_DOWN && action == GLFW_PRESS)
y+=10;
if (key == GLFW_KEY_LEFT && action == GLFW_PRESS)
x-=10;
if (key == GLFW_KEY_RIGHT && action == GLFW_PRESS)
x+=10;
}
Selanjutnya, mirip dengan sebelumnya, kita panggil fungsi doIfKeyPressed
di dalam fungsi key_callback
. Setelah itu, Anda dapat menjalankan program dan menggerakkan kotak Anda ke atas, bawah, kiri, dan kanan dengan menggunakan tombol panah pada keyboard. Untuk tombol lainnya, Anda dapat cek aliasnya pada halaman glfw.org/docs/latest/group__keys.html.
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){
rect.doIfKeyPressed(key, action);
}
Modifikasi program tersebut untuk menerapkan interaksi berikut:
- Posisi rectangle selalu sama dengan posisi kursor.
- Mensimulasikan gerakan drag and drop pada rectangle.