/**************************************************************************** * apps/examples/tinygl/tinygl_main.c * * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /* The gear drawing code is based on the classic "gears" demo by Brian Paul, * which is in the public domain, as shipped with TinyGL in * Raw_Demos/gears.c. */ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #ifndef M_PI # define M_PI 3.14159265358979323846 #endif #if TGL_FEATURE_RENDER_BITS == 32 # define TINYGL_FB_FMT FB_FMT_RGB32 # define TINYGL_ZB_MODE ZB_MODE_RGBA #else # define TINYGL_FB_FMT FB_FMT_RGB16_565 # define TINYGL_ZB_MODE ZB_MODE_5R6G5B #endif /**************************************************************************** * Private Types ****************************************************************************/ struct tinygl_fb_s { int fd; FAR uint8_t *fbmem; struct fb_videoinfo_s vinfo; struct fb_planeinfo_s pinfo; }; /**************************************************************************** * Private Data ****************************************************************************/ static GLfloat g_view_rotx = 20.0f; static GLfloat g_view_roty = 30.0f; static GLfloat g_angle; static GLint g_gear1; static GLint g_gear2; static GLint g_gear3; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: gear * * Description: * Draw a gear wheel into the current display list. * * Input Parameters: * inner_radius - radius of the hole at the center * outer_radius - radius at the center of the teeth * width - width of the gear * teeth - number of teeth * tooth_depth - depth of a tooth * ****************************************************************************/ static void gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, GLint teeth, GLfloat tooth_depth) { GLfloat r0 = inner_radius; GLfloat r1 = outer_radius - tooth_depth / 2.0f; GLfloat r2 = outer_radius + tooth_depth / 2.0f; GLfloat da = 2.0f * M_PI / teeth / 4.0f; GLfloat angle; GLfloat u; GLfloat v; GLfloat len; GLint i; glNormal3f(0.0f, 0.0f, 1.0f); /* Front face */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= teeth; i++) { angle = i * 2.0f * M_PI / teeth; glVertex3f(r0 * cosf(angle), r0 * sinf(angle), width * 0.5f); glVertex3f(r1 * cosf(angle), r1 * sinf(angle), width * 0.5f); glVertex3f(r0 * cosf(angle), r0 * sinf(angle), width * 0.5f); glVertex3f(r1 * cosf(angle + 3 * da), r1 * sinf(angle + 3 * da), width * 0.5f); } glEnd(); /* Front sides of the teeth */ glBegin(GL_QUADS); for (i = 0; i < teeth; i++) { angle = i * 2.0f * M_PI / teeth; glVertex3f(r1 * cosf(angle), r1 * sinf(angle), width * 0.5f); glVertex3f(r2 * cosf(angle + da), r2 * sinf(angle + da), width * 0.5f); glVertex3f(r2 * cosf(angle + 2 * da), r2 * sinf(angle + 2 * da), width * 0.5f); glVertex3f(r1 * cosf(angle + 3 * da), r1 * sinf(angle + 3 * da), width * 0.5f); } glEnd(); glNormal3f(0.0f, 0.0f, -1.0f); /* Back face */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= teeth; i++) { angle = i * 2.0f * M_PI / teeth; glVertex3f(r1 * cosf(angle), r1 * sinf(angle), -width * 0.5f); glVertex3f(r0 * cosf(angle), r0 * sinf(angle), -width * 0.5f); glVertex3f(r1 * cosf(angle + 3 * da), r1 * sinf(angle + 3 * da), -width * 0.5f); glVertex3f(r0 * cosf(angle), r0 * sinf(angle), -width * 0.5f); } glEnd(); /* Back sides of the teeth */ glBegin(GL_QUADS); for (i = 0; i < teeth; i++) { angle = i * 2.0f * M_PI / teeth; glVertex3f(r1 * cosf(angle + 3 * da), r1 * sinf(angle + 3 * da), -width * 0.5f); glVertex3f(r2 * cosf(angle + 2 * da), r2 * sinf(angle + 2 * da), -width * 0.5f); glVertex3f(r2 * cosf(angle + da), r2 * sinf(angle + da), -width * 0.5f); glVertex3f(r1 * cosf(angle), r1 * sinf(angle), -width * 0.5f); } glEnd(); /* Outward faces of the teeth */ glBegin(GL_QUAD_STRIP); for (i = 0; i < teeth; i++) { angle = i * 2.0f * M_PI / teeth; glVertex3f(r1 * cosf(angle), r1 * sinf(angle), width * 0.5f); glVertex3f(r1 * cosf(angle), r1 * sinf(angle), -width * 0.5f); u = r2 * cosf(angle + da) - r1 * cosf(angle); v = r2 * sinf(angle + da) - r1 * sinf(angle); len = sqrtf(u * u + v * v); u /= len; v /= len; glNormal3f(v, -u, 0.0f); glVertex3f(r2 * cosf(angle + da), r2 * sinf(angle + da), width * 0.5f); glVertex3f(r2 * cosf(angle + da), r2 * sinf(angle + da), -width * 0.5f); glNormal3f(cosf(angle), sinf(angle), 0.0f); glVertex3f(r2 * cosf(angle + 2 * da), r2 * sinf(angle + 2 * da), width * 0.5f); glVertex3f(r2 * cosf(angle + 2 * da), r2 * sinf(angle + 2 * da), -width * 0.5f); u = r1 * cosf(angle + 3 * da) - r2 * cosf(angle + 2 * da); v = r1 * sinf(angle + 3 * da) - r2 * sinf(angle + 2 * da); glNormal3f(v, -u, 0.0f); glVertex3f(r1 * cosf(angle + 3 * da), r1 * sinf(angle + 3 * da), width * 0.5f); glVertex3f(r1 * cosf(angle + 3 * da), r1 * sinf(angle + 3 * da), -width * 0.5f); glNormal3f(cosf(angle), sinf(angle), 0.0f); } glVertex3f(r1, 0.0f, width * 0.5f); glVertex3f(r1, 0.0f, -width * 0.5f); glEnd(); /* Inside radius cylinder */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= teeth; i++) { angle = i * 2.0f * M_PI / teeth; glNormal3f(-cosf(angle), -sinf(angle), 0.0f); glVertex3f(r0 * cosf(angle), r0 * sinf(angle), -width * 0.5f); glVertex3f(r0 * cosf(angle), r0 * sinf(angle), width * 0.5f); } glEnd(); } /**************************************************************************** * Name: gears_init ****************************************************************************/ static void gears_init(int width, int height) { static GLfloat pos[4] = { 5.0f, 5.0f, 10.0f, 0.0f }; static GLfloat red[4] = { 0.8f, 0.1f, 0.0f, 1.0f }; static GLfloat green[4] = { 0.0f, 0.8f, 0.2f, 1.0f }; static GLfloat blue[4] = { 0.2f, 0.2f, 1.0f, 1.0f }; static GLfloat white[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; static GLfloat shininess = 5.0f; GLfloat h = (GLfloat)height / (GLfloat)width; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0f, 0.0f, -45.0f); glLightfv(GL_LIGHT0, GL_POSITION, pos); glLightfv(GL_LIGHT0, GL_DIFFUSE, white); glLightfv(GL_LIGHT0, GL_SPECULAR, white); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); /* Build one display list per gear */ g_gear1 = glGenLists(1); glNewList(g_gear1, GL_COMPILE); glMaterialfv(GL_FRONT, GL_DIFFUSE, red); glMaterialfv(GL_FRONT, GL_SPECULAR, white); glMaterialfv(GL_FRONT, GL_SHININESS, &shininess); glColor3fv(red); gear(1.0f, 4.0f, 1.0f, 20, 0.7f); glEndList(); g_gear2 = glGenLists(1); glNewList(g_gear2, GL_COMPILE); glMaterialfv(GL_FRONT, GL_DIFFUSE, green); glMaterialfv(GL_FRONT, GL_SPECULAR, white); glColor3fv(green); gear(0.5f, 2.0f, 2.0f, 10, 0.7f); glEndList(); g_gear3 = glGenLists(1); glNewList(g_gear3, GL_COMPILE); glMaterialfv(GL_FRONT, GL_DIFFUSE, blue); glMaterialfv(GL_FRONT, GL_SPECULAR, white); glColor3fv(blue); gear(1.3f, 2.0f, 0.5f, 10, 0.7f); glEndList(); } /**************************************************************************** * Name: gears_draw ****************************************************************************/ static void gears_draw(void) { g_angle += 2.0f; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(g_view_rotx, 1.0f, 0.0f, 0.0f); glRotatef(g_view_roty, 0.0f, 1.0f, 0.0f); glPushMatrix(); glTranslatef(-3.0f, -2.0f, 0.0f); glRotatef(g_angle, 0.0f, 0.0f, 1.0f); glCallList(g_gear1); glPopMatrix(); glPushMatrix(); glTranslatef(3.1f, -2.0f, 0.0f); glRotatef(-2.0f * g_angle - 9.0f, 0.0f, 0.0f, 1.0f); glCallList(g_gear2); glPopMatrix(); glPushMatrix(); glTranslatef(-3.1f, 4.2f, 0.0f); glRotatef(-2.0f * g_angle - 25.0f, 0.0f, 0.0f, 1.0f); glCallList(g_gear3); glPopMatrix(); glPopMatrix(); } /**************************************************************************** * Name: tinygl_fb_open ****************************************************************************/ static int tinygl_fb_open(FAR const char *path, FAR struct tinygl_fb_s *fb) { int ret; fb->fd = open(path, O_RDWR); if (fb->fd < 0) { ret = -errno; fprintf(stderr, "ERROR: open(%s) failed: %d\n", path, ret); return ret; } if (ioctl(fb->fd, FBIOGET_VIDEOINFO, (unsigned long)((uintptr_t)&fb->vinfo)) < 0 || ioctl(fb->fd, FBIOGET_PLANEINFO, (unsigned long)((uintptr_t)&fb->pinfo)) < 0) { ret = -errno; fprintf(stderr, "ERROR: FBIOGET_VIDEOINFO/PLANEINFO failed: %d\n", ret); goto errout; } if (fb->vinfo.fmt != TINYGL_FB_FMT) { fprintf(stderr, "ERROR: framebuffer format %d, TinyGL needs %d\n", fb->vinfo.fmt, TINYGL_FB_FMT); ret = -EINVAL; goto errout; } fb->fbmem = mmap(NULL, fb->pinfo.fblen, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fb->fd, 0); if (fb->fbmem == MAP_FAILED) { ret = -errno; fprintf(stderr, "ERROR: mmap() failed: %d\n", ret); goto errout; } return OK; errout: close(fb->fd); return ret; } /**************************************************************************** * Name: tinygl_fb_update ****************************************************************************/ static void tinygl_fb_update(FAR struct tinygl_fb_s *fb, FAR ZBuffer *zb) { ZB_copyFrameBuffer(zb, fb->fbmem, fb->pinfo.stride); #ifdef CONFIG_FB_UPDATE struct fb_area_s area; area.x = 0; area.y = 0; area.w = zb->xsize; area.h = zb->ysize; ioctl(fb->fd, FBIO_UPDATE, (unsigned long)((uintptr_t)&area)); #endif } /**************************************************************************** * Name: now_ms ****************************************************************************/ static uint32_t now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: main * * Description: * tinygl [frames] [fbdev] * * Render the TinyGL gears demo on a framebuffer. "frames" is the number * of frames to draw (0, the default, runs forever) and "fbdev" the * framebuffer device (CONFIG_EXAMPLES_TINYGL_DEFAULTFB by default). * ****************************************************************************/ int main(int argc, FAR char *argv[]) { FAR const char *fbdev = CONFIG_EXAMPLES_TINYGL_DEFAULTFB; struct tinygl_fb_s fb; FAR ZBuffer *zb; uint32_t start; uint32_t now; int frames = 0; int count = 0; int total = 0; int ret; if (argc > 1) { frames = atoi(argv[1]); } if (argc > 2) { fbdev = argv[2]; } ret = tinygl_fb_open(fbdev, &fb); if (ret < 0) { return EXIT_FAILURE; } printf("tinygl: %s %dx%d, %d bpp, stride %" PRIu32 "\n", fbdev, fb.vinfo.xres, fb.vinfo.yres, fb.pinfo.bpp, fb.pinfo.stride); /* Let TinyGL render into its own buffer and copy each finished frame to * the framebuffer, so that a half drawn frame is never visible. */ zb = ZB_open(fb.vinfo.xres, fb.vinfo.yres, TINYGL_ZB_MODE, NULL); if (zb == NULL) { fprintf(stderr, "ERROR: ZB_open() failed\n"); ret = EXIT_FAILURE; goto errout_with_fb; } glInit(zb); gears_init(zb->xsize, zb->ysize); start = now_ms(); while (frames == 0 || total < frames) { gears_draw(); tinygl_fb_update(&fb, zb); count++; total++; now = now_ms(); if (now - start >= 5000) { printf("tinygl: %d frames in %" PRIu32 " ms = %d.%d FPS\n", count, now - start, count * 1000 / (int)(now - start), (count * 10000 / (int)(now - start)) % 10); count = 0; start = now; } } printf("tinygl: rendered %d frames\n", total); glDeleteList(g_gear1); glDeleteList(g_gear2); glDeleteList(g_gear3); glClose(); ZB_close(zb); ret = EXIT_SUCCESS; errout_with_fb: munmap(fb.fbmem, fb.pinfo.fblen); close(fb.fd); return ret; }