/*
MiniGLUT - minimal GLUT subset without dependencies
-Copyright (C) 2020 John Tsiombikas <nuclear@member.fsf.org>
+Copyright (C) 2020-2022 John Tsiombikas <nuclear@member.fsf.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-#if defined(__unix__)
+#if defined(unix) || defined(__unix__)
#include <X11/Xlib.h>
#include <X11/keysym.h>
static Display *dpy;
static Window win, root;
+static Colormap cmap;
+static int cmap_size;
static int scr;
static GLXContext ctx;
static Atom xa_wm_proto, xa_wm_del_win;
static Atom xa_motif_wm_hints;
static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
static unsigned int evmask;
+static Cursor blank_cursor;
static int have_netwm_fullscr(void);
#include <windows.h>
#define BUILD_WIN32
-static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
+static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
static HINSTANCE hinst;
static HWND win;
static HDC dc;
static HGLRC ctx;
+static HPALETTE cmap;
+static int cmap_size;
#else
#error unsupported platform
#include <GL/gl.h>
#include "miniglut.h"
+#ifdef _MSC_VER
+#pragma warning (disable: 4244 4305)
+#endif
+
+
struct ctx_info {
int rsize, gsize, bsize, asize;
int zsize, ssize;
static struct ctx_info ctx_info;
static int cur_cursor = GLUT_CURSOR_INHERIT;
+static int ignore_key_repeat;
static glut_cb cb_display;
static glut_cb cb_idle;
void glutInit(int *argc, char **argv)
{
#ifdef BUILD_X11
+ Pixmap blankpix = 0;
+ XColor xcol;
+
if(!(dpy = XOpenDisplay(0))) {
panic("Failed to connect to the X server\n");
}
xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
+ xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
if(have_netwm_fullscr()) {
xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
- xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
}
xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
evmask = ExposureMask | StructureNotifyMask;
+ if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
+ blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
+ XFreePixmap(dpy, blankpix);
+ }
+
#endif
#ifdef BUILD_WIN32
WNDCLASSEX wc = {0};
upd_pending = 1;
}
+void glutIgnoreKeyRepeat(int ignore)
+{
+ ignore_key_repeat = ignore;
+}
+
#define UPD_EVMASK(x) \
do { \
if(func) { \
return ctx_info.srgb;
case GLUT_WINDOW_CURSOR:
return cur_cursor;
+ case GLUT_WINDOW_COLORMAP_SIZE:
+ return cmap_size;
case GLUT_SCREEN_WIDTH:
get_screen_size(&x, &y);
return x;
case XK_Linefeed:
return '\r';
case XK_Return:
- return '\n';
+ return '\r';
case XK_Delete:
return 127;
case XK_Tab:
break;
case KeyPress:
+ if(0) {
case KeyRelease:
+ if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) {
+ XEvent next;
+ XPeekEvent(dpy, &next);
+
+ if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode &&
+ next.xkey.time == ev->xkey.time) {
+ /* this is a key-repeat event, ignore the release and consume
+ * the following press
+ */
+ XNextEvent(dpy, &next);
+ break;
+ }
+ }
+ }
modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
break;
int fmt;
long offs = 0;
unsigned long i, count, rem;
- Atom prop[8], type;
+ Atom *prop, type;
Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
do {
XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
- &type, &fmt, &count, &rem, (unsigned char**)prop);
+ &type, &fmt, &count, &rem, (unsigned char**)&prop);
for(i=0; i<count; i++) {
if(prop[i] == xa_net_wm_state_fullscr) {
+ XFree(prop);
return 1;
}
}
+ XFree(prop);
offs += count;
} while(rem > 0);
case GLUT_CURSOR_INHERIT:
break;
case GLUT_CURSOR_NONE:
- /* TODO */
+ cur = blank_cursor;
+ break;
default:
return;
}
cur_cursor = cidx;
}
+void glutWarpPointer(int x, int y)
+{
+ XWarpPointer(dpy, None, win, 0, 0, 0, 0, x, y);
+}
+
+void glutSetColor(int idx, float r, float g, float b)
+{
+ XColor color;
+
+ if(idx >= 0 && idx < cmap_size) {
+ color.pixel = idx;
+ color.red = (unsigned short)(r * 65535.0f);
+ color.green = (unsigned short)(g * 65535.0f);
+ color.blue = (unsigned short)(b * 65535.0f);
+ color.flags = DoRed | DoGreen | DoBlue;
+ XStoreColor(dpy, cmap, &color);
+ }
+}
+
+float glutGetColor(int idx, int comp)
+{
+ XColor color;
+
+ if(idx < 0 || idx >= cmap_size) {
+ return -1.0f;
+ }
+
+ color.pixel = idx;
+ XQueryColor(dpy, cmap, &color);
+ switch(comp) {
+ case GLUT_RED:
+ return color.red / 65535.0f;
+ case GLUT_GREEN:
+ return color.green / 65535.0f;
+ case GLUT_BLUE:
+ return color.blue / 65535.0f;
+ default:
+ break;
+ }
+ return -1.0f;
+}
+
+void glutSetKeyRepeat(int repmode)
+{
+ if(repmode) {
+ XAutoRepeatOn(dpy);
+ } else {
+ XAutoRepeatOff(dpy);
+ }
+}
+
static XVisualInfo *choose_visual(unsigned int mode)
{
XVisualInfo *vi;
*aptr++ = 1;
} else {
*aptr++ = GLX_RGBA;
- *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
- *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
- *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
+ *aptr++ = GLX_RED_SIZE; *aptr++ = 1;
+ *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1;
+ *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1;
}
if(mode & GLUT_ALPHA) {
*aptr++ = GLX_ALPHA_SIZE;
}
if(mode & GLUT_DEPTH) {
*aptr++ = GLX_DEPTH_SIZE;
- *aptr++ = 16;
+ *aptr++ = 8;
}
if(mode & GLUT_STENCIL) {
*aptr++ = GLX_STENCIL_SIZE;
glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
+ if(!(cmap = XCreateColormap(dpy, root, vi->visual, mode & GLUT_INDEX ? AllocAll : AllocNone))) {
+ XFree(vi);
+ glXDestroyContext(dpy, ctx);
+ panic("Failed to create colormap\n");
+ }
+ cmap_size = GLUT_INDEX ? vi->colormap_size : 0;
+
xattr.background_pixel = BlackPixel(dpy, scr);
- xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
+ xattr.colormap = cmap;
xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
XFree(vi);
glXDestroyContext(dpy, ctx);
+ XFreeColormap(dpy, cmap);
panic("Failed to create window\n");
}
XFree(vi);
win = *(Window*)prop;
XFree(prop);
+ wname.value = 0;
if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
- return 0;
+ win = 0;
}
+ XFree(wname.value);
return win;
}
SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
}
+static void calc_win_rect(RECT *rect, int x, int y, int w, int h)
+{
+ rect->left = x;
+ rect->top = y;
+ rect->right = x + w;
+ rect->bottom = y + h;
+ AdjustWindowRect(rect, WS_OVERLAPPEDWINDOW, 0);
+}
+
void glutReshapeWindow(int xsz, int ysz)
{
RECT rect;
unsigned int flags = SWP_SHOWWINDOW;
if(fullscreen) {
- rect.left = prev_win_x;
- rect.top = prev_win_y;
+ calc_win_rect(&rect, prev_win_x, prev_win_y, xsz, ysz);
SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
fullscreen = 0;
flags |= SWP_FRAMECHANGED;
} else {
GetWindowRect(win, &rect);
+ calc_win_rect(&rect, rect.left, rect.top, xsz, ysz);
}
+
+ xsz = rect.right - rect.left;
+ ysz = rect.bottom - rect.top;
SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
}
}
}
+void glutWarpPointer(int x, int y)
+{
+ POINT pt;
+ pt.x = x;
+ pt.y = y;
+
+ ClientToScreen(win, &pt);
+ SetCursorPos(pt.x, pt.y);
+}
+
+void glutSetColor(int idx, float r, float g, float b)
+{
+ PALETTEENTRY col;
+
+ if(idx < 0 || idx >= 256 || !cmap) {
+ return;
+ }
+
+ col.peRed = (int)(r * 255.0f);
+ col.peGreen = (int)(g * 255.0f);
+ col.peBlue = (int)(b * 255.0f);
+ col.peFlags = PC_NOCOLLAPSE;
+
+ SetPaletteEntries(cmap, idx, 1, &col);
+
+ if(dc) {
+ UnrealizeObject(cmap);
+ SelectPalette(dc, cmap, 0);
+ RealizePalette(dc);
+ }
+}
+
+float glutGetColor(int idx, int comp)
+{
+ PALETTEENTRY col;
+
+ if(idx < 0 || idx >= 256 || !cmap) {
+ return -1.0f;
+ }
+
+ if(!GetPaletteEntries(cmap, idx, 1, &col)) {
+ return -1.0f;
+ }
+
+ switch(comp) {
+ case GLUT_RED:
+ return col.peRed / 255.0f;
+ case GLUT_GREEN:
+ return col.peGreen / 255.0f;
+ case GLUT_BLUE:
+ return col.peBlue / 255.0f;
+ default:
+ break;
+ }
+ return -1.0f;
+}
+
+void glutSetKeyRepeat(int repmode)
+{
+}
+
#define WGL_DRAW_TO_WINDOW 0x2001
+#define WGL_ACCELERATION 0x2003
#define WGL_SUPPORT_OPENGL 0x2010
#define WGL_DOUBLE_BUFFER 0x2011
#define WGL_STEREO 0x2012
#define WGL_ACCUM_BITS 0x201d
#define WGL_DEPTH_BITS 0x2022
#define WGL_STENCIL_BITS 0x2023
+#define WGL_FULL_ACCELERATION 0x2027
#define WGL_TYPE_RGBA 0x202b
#define WGL_TYPE_COLORINDEX 0x202c
static unsigned int choose_pixfmt(unsigned int mode)
{
unsigned int num_pixfmt, pixfmt = 0;
- int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1 };
+ int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
+ WGL_ACCELERATION, WGL_FULL_ACCELERATION };
+ float fattr[2] = {0, 0};
- int *aptr = attr;
+ int *aptr = attr + 6;
int *samples = 0;
if(mode & GLUT_DOUBLE) {
}
ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
- ATTR(WGL_COLOR_BITS, 8);
+ ATTR(WGL_COLOR_BITS, mode & GLUT_INDEX ? 8 : 24);
if(mode & GLUT_ALPHA) {
ATTR(WGL_ALPHA_BITS, 4);
}
}
*aptr++ = 0;
- while(!wglChoosePixelFormat(dc, attr, 0, 1, &pixfmt, &num_pixfmt) && samples && *samples) {
+ while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
*samples >>= 1;
if(!*samples) {
aptr[-3] = 0;
return pixfmt;
}
+static PIXELFORMATDESCRIPTOR pfd;
static PIXELFORMATDESCRIPTOR tmppfd = {
sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
}
tmpdc = GetDC(tmpwin);
- if(!(pixfmt = ChoosePixelFormat(dc, &tmppfd)) ||
- !SetPixelFormat(dc, pixfmt, &tmppfd) ||
+ if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
+ !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
!(tmpctx = wglCreateContext(tmpdc))) {
goto fail;
}
if(!(pixfmt = choose_pixfmt(init_mode))) {
panic("Failed to find suitable pixel format\n");
}
- if(!SetPixelFormat(dc, pixfmt, &tmppfd)) {
+ if(!SetPixelFormat(dc, pixfmt, &pfd)) {
panic("Failed to set the selected pixel format\n");
}
if(!(ctx = wglCreateContext(dc))) {
GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
+
return 0;
fail:
return -1;
}
-
static void create_window(const char *title)
{
- int pixfmt;
- PIXELFORMATDESCRIPTOR pfd = {0};
RECT rect;
- int width, height;
+ int i, pixfmt, width, height;
+ char palbuf[sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)];
+ LOGPALETTE *logpal;
- rect.left = init_x;
- rect.top = init_y;
- rect.right = init_x + init_width;
- rect.bottom = init_y + init_height;
- AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
+ calc_win_rect(&rect, init_x, init_y, init_width, init_height);
width = rect.right - rect.left;
height = rect.bottom - rect.top;
- if(create_window_wglext(title, width, height) == -1) {
-
- if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
- rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
- panic("Failed to create window\n");
- }
- dc = GetDC(win);
-
- pfd.nSize = sizeof pfd;
- pfd.nVersion = 1;
- pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
- if(init_mode & GLUT_STEREO) {
- pfd.dwFlags |= PFD_STEREO;
- }
- pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
+ memset(&pfd, 0, sizeof pfd);
+ pfd.nSize = sizeof pfd;
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ if(init_mode & GLUT_STEREO) {
+ pfd.dwFlags |= PFD_STEREO;
+ }
+ if(init_mode & GLUT_INDEX) {
+ pfd.iPixelType = PFD_TYPE_COLORINDEX;
+ pfd.cColorBits = 8;
+ } else {
+ pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
- if(init_mode & GLUT_ALPHA) {
- pfd.cAlphaBits = 8;
- }
- if(init_mode & GLUT_ACCUM) {
- pfd.cAccumBits = 24;
- }
- if(init_mode & GLUT_DEPTH) {
- pfd.cDepthBits = 24;
- }
- if(init_mode & GLUT_STENCIL) {
- pfd.cStencilBits = 8;
- }
- pfd.iLayerType = PFD_MAIN_PLANE;
+ }
+ if(init_mode & GLUT_ALPHA) {
+ pfd.cAlphaBits = 8;
+ }
+ if(init_mode & GLUT_ACCUM) {
+ pfd.cAccumBits = 24;
+ }
+ if(init_mode & GLUT_DEPTH) {
+ pfd.cDepthBits = 24;
+ }
+ if(init_mode & GLUT_STENCIL) {
+ pfd.cStencilBits = 8;
+ }
+ pfd.iLayerType = PFD_MAIN_PLANE;
- if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
- panic("Failed to find suitable pixel format\n");
- }
- if(!SetPixelFormat(dc, pixfmt, &pfd)) {
- panic("Failed to set the selected pixel format\n");
+ if(init_mode & (GLUT_SRGB | GLUT_MULTISAMPLE)) {
+ if(create_window_wglext(title, width, height) != -1) {
+ goto ctxdone;
}
- if(!(ctx = wglCreateContext(dc))) {
- panic("Failed to create the OpenGL context\n");
- }
- wglMakeCurrent(dc, ctx);
+ }
- DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
- ctx_info.rsize = pfd.cRedBits;
- ctx_info.gsize = pfd.cGreenBits;
- ctx_info.bsize = pfd.cBlueBits;
- ctx_info.asize = pfd.cAlphaBits;
- ctx_info.zsize = pfd.cDepthBits;
- ctx_info.ssize = pfd.cStencilBits;
- ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
- ctx_info.samples = 0;
- ctx_info.srgb = 0;
+ /* if we don't need sRGB or multisample, or if the wglChoosePixelFormat method
+ * failed, just use the old-style ChoosePixelFormat method instead
+ */
+ if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
+ rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
+ panic("Failed to create window\n");
}
+ dc = GetDC(win);
+ if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
+ panic("Failed to find suitable pixel format\n");
+ }
+ if(!SetPixelFormat(dc, pixfmt, &pfd)) {
+ panic("Failed to set the selected pixel format\n");
+ }
+ if(!(ctx = wglCreateContext(dc))) {
+ panic("Failed to create the OpenGL context\n");
+ }
+ wglMakeCurrent(dc, ctx);
+
+ DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
+ ctx_info.rsize = pfd.cRedBits;
+ ctx_info.gsize = pfd.cGreenBits;
+ ctx_info.bsize = pfd.cBlueBits;
+ ctx_info.asize = pfd.cAlphaBits;
+ ctx_info.zsize = pfd.cDepthBits;
+ ctx_info.ssize = pfd.cStencilBits;
+ ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
+ ctx_info.samples = 0;
+ ctx_info.srgb = 0;
+
+ctxdone:
ShowWindow(win, 1);
SetForegroundWindow(win);
SetFocus(win);
+
+ if(init_mode & GLUT_INDEX) {
+ logpal = (LOGPALETTE*)palbuf;
+
+ GetSystemPaletteEntries(dc, 0, 256, logpal->palPalEntry);
+
+ logpal->palVersion = 0x300;
+ logpal->palNumEntries = 256;
+
+ if(!(cmap = CreatePalette(logpal))) {
+ panic("Failed to create palette in indexed mode");
+ }
+ SelectPalette(dc, cmap, 0);
+ RealizePalette(dc);
+
+ cmap_size = 256;
+ } else {
+ if(GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES) <= 8) {
+ /* for RGB mode in 8bpp displays we also need to set up a palette
+ * with RGB 332 colors
+ */
+ logpal = (LOGPALETTE*)palbuf;
+
+ logpal->palVersion = 0x300;
+ logpal->palNumEntries = 256;
+
+ for(i=0; i<256; i++) {
+ int r = i & 7;
+ int g = (i >> 3) & 7;
+ int b = (i >> 5) & 3;
+
+ logpal->palPalEntry[i].peRed = (r << 5) | (r << 2) | (r >> 1);
+ logpal->palPalEntry[i].peGreen = (g << 5) | (g << 2) | (g >> 1);
+ logpal->palPalEntry[i].peBlue = (b << 6) | (b << 4) | (b << 2) | b;
+ logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
+ }
+
+ if((cmap = CreatePalette(logpal))) {
+ SelectPalette(dc, cmap, 0);
+ RealizePalette(dc);
+ cmap_size = 256;
+ }
+ }
+ }
+
upd_pending = 1;
reshape_pending = 1;
}
-static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
+static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
{
static int mouse_x, mouse_y;
int x, y, key;
}
}
+#ifndef VK_OEM_1
+#define VK_OEM_1 0xba
+#define VK_OEM_2 0xbf
+#define VK_OEM_3 0xc0
+#define VK_OEM_4 0xdb
+#define VK_OEM_5 0xdc
+#define VK_OEM_6 0xdd
+#define VK_OEM_7 0xde
+#endif
+
static int translate_vkey(int vkey)
{
switch(vkey) {
case VK_UP: return GLUT_KEY_UP;
case VK_RIGHT: return GLUT_KEY_RIGHT;
case VK_DOWN: return GLUT_KEY_DOWN;
+ case VK_OEM_1: return ';';
+ case VK_OEM_2: return '/';
+ case VK_OEM_3: return '`';
+ case VK_OEM_4: return '[';
+ case VK_OEM_5: return '\\';
+ case VK_OEM_6: return ']';
+ case VK_OEM_7: return '\'';
default:
break;
}
}
#endif /* BUILD_WIN32 */
-#if defined(__unix__) || defined(__APPLE__)
+#if defined(unix) || defined(__unix__) || defined(__APPLE__)
#include <sys/time.h>
#ifdef MINIGLUT_USE_LIBC
#ifdef MINIGLUT_USE_LIBC
+#include <stdlib.h>
+#ifdef _WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
static void sys_exit(int status)
{
exit(status);
glPopAttrib();
}
+#define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
+#define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
+
+#define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
+
+#define PATCH_SUBDIV 7
+
+static float teapot_part_flip[] = {
+ 1, 1, 1, 1, /* rim flip */
+ 1, 1, 1, 1, /* body1 flip */
+ 1, 1, 1, 1, /* body2 flip */
+ 1, 1, 1, 1, /* lid patch 1 flip */
+ 1, 1, 1, 1, /* lid patch 2 flip */
+ 1, -1, /* handle 1 flip */
+ 1, -1, /* handle 2 flip */
+ 1, -1, /* spout 1 flip */
+ 1, -1, /* spout 2 flip */
+ 1, 1, 1, 1 /* bottom flip */
+};
+
+static float teapot_part_rot[] = {
+ 0, 90, 180, 270, /* rim rotations */
+ 0, 90, 180, 270, /* body patch 1 rotations */
+ 0, 90, 180, 270, /* body patch 2 rotations */
+ 0, 90, 180, 270, /* lid patch 1 rotations */
+ 0, 90, 180, 270, /* lid patch 2 rotations */
+ 0, 0, /* handle 1 rotations */
+ 0, 0, /* handle 2 rotations */
+ 0, 0, /* spout 1 rotations */
+ 0, 0, /* spout 2 rotations */
+ 0, 90, 180, 270 /* bottom rotations */
+};
+
+
+static int teapot_index[] = {
+ /* rim */
+ 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ /* body1 */
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ /* body 2 */
+ 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ /* lid 1 */
+ 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
+ 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
+ 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
+ 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
+ /* lid 2 */
+ 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ /* handle 1 */
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* handle 2 */
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
+ /* spout 1 */
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ /* spout 2 */
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ /* bottom */
+ 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
+ 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
+ 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
+ 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
+};
+
+
+static float teapot_verts[][3] = {
+ { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
+ { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
+ { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
+ { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
+ { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
+ { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
+ { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
+ { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
+ { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
+ { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
+ { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
+ { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
+ { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
+ { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
+ { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
+ { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
+ { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
+ { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
+ { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
+ { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
+ { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
+ { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
+ { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
+ { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
+ { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
+ { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
+ { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
+ { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
+ { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
+ { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
+ { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
+ { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
+ { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
+ { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
+ { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
+ { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
+ { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
+ { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
+ { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
+ { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
+ { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
+ { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
+ { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
+ { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
+ { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
+ { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
+ { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
+ { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
+ { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
+ { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
+ { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
+ { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
+ { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
+ { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
+ { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
+ { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
+ { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
+ { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
+ { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
+ { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
+ { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
+ { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
+ { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
+ { 0.8400, -1.5000, 0.07500 }
+};
+
+static void draw_patch(int *index, int flip, float scale);
+static float bernstein(int i, float x);
+
void glutSolidTeapot(float size)
{
+ int i;
+
+ size /= 2.0;
+
+ for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
+ float flip = teapot_part_flip[i];
+ float rot = teapot_part_rot[i];
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glTranslatef(0, -3.15 * size * 0.5, 0);
+ glRotatef(rot, 0, 1, 0);
+ glScalef(1, 1, flip);
+ glRotatef(-90, 1, 0, 0);
+
+ draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
+
+ glPopMatrix();
+ }
}
void glutWireTeapot(float size)
{
+ glPushAttrib(GL_POLYGON_BIT);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ glutSolidTeapot(size);
+ glPopAttrib();
+}
+
+
+static void bezier_patch(float *res, float *cp, float u, float v)
+{
+ int i, j;
+
+ res[0] = res[1] = res[2] = 0.0f;
+
+ for(j=0; j<4; j++) {
+ for(i=0; i<4; i++) {
+ float bu = bernstein(i, u);
+ float bv = bernstein(j, v);
+
+ res[0] += cp[0] * bu * bv;
+ res[1] += cp[1] * bu * bv;
+ res[2] += cp[2] * bu * bv;
+
+ cp += 3;
+ }
+ }
+}
+
+static float rsqrt(float x)
+{
+ float xhalf = x * 0.5f;
+ int i = *(int*)&x;
+ i = 0x5f3759df - (i >> 1);
+ x = *(float*)&i;
+ x = x * (1.5f - xhalf * x * x);
+ return x;
+}
+
+
+#define CROSS(res, a, b) \
+ do { \
+ (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
+ (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
+ (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
+ } while(0)
+
+#define NORMALIZE(v) \
+ do { \
+ float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
+ (v)[0] *= s; \
+ (v)[1] *= s; \
+ (v)[2] *= s; \
+ } while(0)
+
+#define DT 0.001
+
+static void bezier_patch_norm(float *res, float *cp, float u, float v)
+{
+ float tang[3], bitan[3], tmp[3];
+
+ bezier_patch(tang, cp, u + DT, v);
+ bezier_patch(tmp, cp, u - DT, v);
+ tang[0] -= tmp[0];
+ tang[1] -= tmp[1];
+ tang[2] -= tmp[2];
+
+ bezier_patch(bitan, cp, u, v + DT);
+ bezier_patch(tmp, cp, u, v - DT);
+ bitan[0] -= tmp[0];
+ bitan[1] -= tmp[1];
+ bitan[2] -= tmp[2];
+
+ CROSS(res, tang, bitan);
+ NORMALIZE(res);
+}
+
+
+
+static float bernstein(int i, float x)
+{
+ float invx = 1.0f - x;
+
+ switch(i) {
+ case 0:
+ return invx * invx * invx;
+ case 1:
+ return 3.0f * x * invx * invx;
+ case 2:
+ return 3.0f * x * x * invx;
+ case 3:
+ return x * x * x;
+ default:
+ break;
+ }
+ return 0.0f;
+}
+
+static void draw_patch(int *index, int flip, float scale)
+{
+ static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
+ static const float voffs[4] = {0, 1, 1, 0};
+
+ int i, j, k;
+ float cp[16 * 3];
+ float pt[3], n[3];
+ float u, v;
+ float du = 1.0 / PATCH_SUBDIV;
+ float dv = 1.0 / PATCH_SUBDIV;
+
+ /* collect control points */
+ for(i=0; i<16; i++) {
+ cp[i * 3] = teapot_verts[index[i]][0];
+ cp[i * 3 + 1] = teapot_verts[index[i]][1];
+ cp[i * 3 + 2] = teapot_verts[index[i]][2];
+ }
+
+ glBegin(GL_QUADS);
+ glColor3f(1, 1, 1);
+
+ u = 0;
+ for(i=0; i<PATCH_SUBDIV; i++) {
+ v = 0;
+ for(j=0; j<PATCH_SUBDIV; j++) {
+
+ for(k=0; k<4; k++) {
+ bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
+
+ /* top/bottom normal hack */
+ if(pt[2] > 3.14) {
+ n[0] = n[1] = 0.0f;
+ n[2] = 1.0f;
+ } else if(pt[2] < 0.00001) {
+ n[0] = n[1] = 0.0f;
+ n[2] = -1.0f;
+ } else {
+ bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
+ }
+
+ glTexCoord2f(u, v);
+ glNormal3fv(n);
+ glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+
+ glEnd();
}