// like star screensaver (ssstar.scr) by OpenGL // // by mieki256 // License : CC0 / Public Domain // Last updated: <2024/01/10 05:31:11 +0900> #include #include #include #include #define _USE_MATH_DEFINES #include #include #include #include // use stb library // https://github.com/nothings/stb #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" // include png image binary #include "texture.h" #define SCRW 1280 #define SCRH 720 #define FPS 60 #define OBJ_MAX 1000 #define USE_DEPTHMASK 0 #define USE_FOG 1 static GLuint texture; static int scr_w = SCRW; static int scr_h = SCRH; static GLfloat speed = 1.0; static GLfloat dist = 300.0; static GLfloat fovy = 90.0; // star object work typedef struct { GLfloat x; GLfloat y; GLfloat z; GLfloat dist; GLfloat spd; int kind; GLfloat tx; GLfloat ty; GLfloat tw; GLfloat th; } star; static star objw[OBJ_MAX]; static DWORD rec_time; static int count_frame; static int count_fps; void initCountFps(void) { timeBeginPeriod(1); rec_time = timeGetTime(); count_fps = 0; count_frame = 0; } void closeCountFps(void) { timeEndPeriod(1); } void calcFps(void) { count_frame++; DWORD t = timeGetTime() - rec_time; if (t >= 1000) { rec_time += 1000; count_fps = count_frame; count_frame = 0; } else if (t < 0) { rec_time = timeGetTime(); count_fps = 0; count_frame = 0; } } static double getRand(void) { return ((double)rand() / RAND_MAX); // retrun 0.0 - 1.0 } void initXYPos(star *o) { float h = o->z * tan((fovy / 2.0) * M_PI / 180.0); float aspect = (float)scr_w / (float)scr_h; o->x = (getRand() * 2.0 - 1.0) * h * aspect; o->y = (getRand() * 2.0 - 1.0) * h; } void initObjs(void) { for (int i = 0; i < OBJ_MAX; i++) { objw[i].dist = dist; objw[i].spd = speed; int k = rand() % 4; objw[i].kind = k; objw[i].tx = 0.5 * (k & 0x01); objw[i].ty = 0.5 * ((k >> 1) & 0x01); objw[i].tw = 0.5; objw[i].th = 0.5; objw[i].z = getRand() * (-1.0 * dist); initXYPos(&(objw[i])); } } void updateObjs(void) { for (int i = 0; i < OBJ_MAX; i++) { objw[i].z += objw[i].spd; if (objw[i].z >= 0.0) { objw[i].z -= objw[i].dist; initXYPos(&(objw[i])); continue; } // get position on screen GLfloat sz = (float)(scr_h / 2.0) / tan((fovy / 2.0) * M_PI / 180.0); GLfloat sx = objw[i].x * sz / objw[i].z; GLfloat sy = objw[i].y * sz / objw[i].z; float wh = (float)(scr_w / 2.0) * 1.2; float hh = (float)(scr_h / 2.0) * 1.2; if (sx < -wh || wh < sx || sy < -hh || hh < sy) { // outside display area objw[i].z = -objw[i].dist - 15.0; initXYPos(&(objw[i])); continue; } } } /** * Load texture from png image on memory * * @param[in] _pngData png binary on memory * @param[in] _pngDataLen png binary size * return GLuint OpenGL texture ID. if 0, process fails */ GLuint createTextureFromPngInMemory(const unsigned char *_pngData, int _pngLen) { GLuint texture; int width = 0, height = 0, bpp = 0; unsigned char *data = NULL; data = stbi_load_from_memory(_pngData, _pngLen, &width, &height, &bpp, 4); // create OpenGL texture glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glClearColor(0, 0, 0, 0); glShadeModel(GL_SMOOTH); // set texture repeat glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // set texture filter glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); // Release allocated all memory stbi_image_free(data); return texture; } // draw OpenGL void drawGL() { updateObjs(); glClearColor(0.0, 0.0, 0.0, 0.0); // background color glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); // Reset The View // set camera gluLookAt(0.0, 0.0, 0.0, 0.0, 0.0, -10.0, 0.0, 1.0, 0.0); glEnable(GL_BLEND); // enable texture glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_ONE, GL_ONE); glColor4f(1.0, 1.0, 1.0, 1.0); // set color // draw objects #if USE_DEPTHMASK == 1 glDepthMask(GL_TRUE); glDepthFunc(GL_LESS); #else glDepthMask(GL_FALSE); glDepthFunc(GL_LEQUAL); #endif GLfloat v = 2.0; for (int i = 0; i < OBJ_MAX; i++) { GLfloat tx, ty, tw, th; star *o = &(objw[i]); tx = o->tx; ty = o->ty; tw = o->tw; th = o->th; glPushMatrix(); glTranslatef(o->x, o->y, o->z); // translate { GLdouble m[16]; glGetDoublev(GL_MODELVIEW_MATRIX, m); m[0] = m[5] = m[10] = 1.0; m[1] = m[2] = m[4] = m[6] = m[8] = m[9] = 0.0; glLoadMatrixd(m); } glBegin(GL_QUADS); glTexCoord2f(tx, ty); // set u, v glVertex3f(-v, -v, 0); // Top Left glTexCoord2f(tx + tw, ty); glVertex3f(v, -v, 0.0); // Top Right glTexCoord2f(tx + tw, ty + th); glVertex3f(v, v, 0.0); // Bottom Right glTexCoord2f(tx, ty + th); glVertex3f(-v, v, 0.0); // Bottom Left glEnd(); glPopMatrix(); } glDisable(GL_TEXTURE_2D); #if USE_DEPTHMASK == 0 glDepthMask(GL_TRUE); glDepthFunc(GL_LESS); #endif // draw FPS text glRasterPos3f(-0.1, 1.0, -1.08); { char s[512]; sprintf(s, "%d/%d FPS", count_fps, FPS); for (int j = 0; j < strlen(s); j++) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, s[j]); } } glutSwapBuffers(); calcFps(); } void initViewportAndPers(int width, int height) { // Prevent A Divide By Zero If The Window Is Too Small if (height == 0) { height = 1; } scr_w = width; scr_h = height; // Reset The Current Viewport And Perspective Transformation glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Reset The Projection Matrix // Calculate The Aspect Ratio Of The Window float aspect = (float)width / (float)height; GLfloat znear = 0.1; GLfloat zfar = dist + 50.0; gluPerspective(fovy, aspect, znear, zfar); glMatrixMode(GL_MODELVIEW); } void initGL(int width, int height) { glClearColor(0.0, 0.0, 0.0, 0.0); // background color (R, G, B, A) glClearDepth(1.0); // Enables Clearing Of The Depth Buffer glEnable(GL_DEPTH_TEST); // Enables Depth Testing glDepthFunc(GL_LESS); // The Type Of Depth Test To Do glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading #if USE_FOG == 1 // set fog GLfloat fog_color[4] = {0.0, 0.0, 0.0, 0.0}; glEnable(GL_FOG); glFogi(GL_FOG_MODE, GL_LINEAR); glFogf(GL_FOG_START, dist * 0.75); glFogf(GL_FOG_END, dist); glFogfv(GL_FOG_COLOR, fog_color); #endif initViewportAndPers(width, height); initCountFps(); } // window resize callback function void resize(int width, int height) { initViewportAndPers(width, height); } void onTimer(int value) { glutPostRedisplay(); // redraw glutTimerFunc((1000 / FPS), onTimer, 0); } // Keyboard callback function void keyboard(unsigned char key, int x, int y) { switch (key) { case '\x1B': case 'q': // Exit on escape or 'q' key press glutLeaveMainLoop(); // exit(EXIT_SUCCESS); break; } } void closeGL(void) { closeCountFps(); } int main(int argc, char **argv) { initObjs(); glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(SCRW, SCRH); // glutInitWindowPosition(0, 0); glutCreateWindow("Like star screensaver"); glutKeyboardFunc(keyboard); glutDisplayFunc(drawGL); glutReshapeFunc(resize); initGL(SCRW, SCRH); // create OpenGL texture from PNG image texture = createTextureFromPngInMemory((unsigned char *)&texture_png, texture_png_len); if (!texture) { fprintf(stderr, "Failed create texture\n"); exit(1); } // glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS); glutTimerFunc((1000 / FPS), onTimer, 0); glutMainLoop(); closeGL(); return EXIT_SUCCESS; }