1
/*
2
 *  The Mana World
3
 *  Copyright (C) 2004  The Mana World Development Team
4
 *
5
 *  This file is part of The Mana World.
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program; if not, write to the Free Software
19
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 */
21
22
#include <SDL.h>
23
24
#include "log.h"
25
#include "openglgraphics.h"
26
27
#include "resources/image.h"
28
29
#ifdef USE_OPENGL
30
31
#ifdef __APPLE__
32
#include <OpenGL/OpenGL.h>
33
#endif
34
35
#ifndef GL_TEXTURE_RECTANGLE_ARB
36
#define GL_TEXTURE_RECTANGLE_ARB 0x84F5
37
#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8
38
#endif
39
40
OpenGLGraphics::OpenGLGraphics():
41
    mAlpha(false), mTexture(false), mColorAlpha(false),
42
    mSync(false)
43
{
44
}
45
46
OpenGLGraphics::~OpenGLGraphics()
47
{
48
}
49
50
void OpenGLGraphics::setSync(bool sync)
51
{
52
    mSync = sync;
53
}
54
55
bool OpenGLGraphics::setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel)
56
{
57
    logger->log("Setting video mode %dx%d %s",
58
            w, h, fs ? "fullscreen" : "windowed");
59
60
    int displayFlags = SDL_ANYFORMAT | SDL_OPENGL;
61
62
    mFullscreen = fs;
63
    mHWAccel = hwaccel;
64
65
    if (fs)
66
        displayFlags |= SDL_FULLSCREEN;
67
68
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
69
70
    if (!(mScreen = SDL_SetVideoMode(w, h, bpp, displayFlags)))
71
        return false;
72
73
#ifdef __APPLE__
74
    if (mSync)
75
    {
76
        const GLint VBL = 1;
77
        CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &VBL);
78
    }
79
#endif
80
81
    // Setup OpenGL
82
    glViewport(0, 0, w, h);
83
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
84
    int gotDoubleBuffer;
85
    SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &gotDoubleBuffer);
86
    logger->log("Using OpenGL %s double buffering.",
87
            (gotDoubleBuffer ? "with" : "without"));
88
89
    char const *glExtensions = (char const *)glGetString(GL_EXTENSIONS);
90
    GLint texSize;
91
    bool rectTex = strstr(glExtensions, "GL_ARB_texture_rectangle");
92
    if (rectTex)
93
    {
94
        Image::mTextureType = GL_TEXTURE_RECTANGLE_ARB;
95
        glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &texSize);
96
    }
97
    else
98
    {
99
        Image::mTextureType = GL_TEXTURE_2D;
100
        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
101
    }
102
    Image::mTextureSize = texSize;
103
    logger->log("OpenGL texture size: %d pixels%s", Image::mTextureSize,
104
                rectTex ? " (rectangle textures)" : "");
105
106
    return true;
107
}
108
109
static inline void drawQuad(Image *image,
110
                            int srcX, int srcY, int dstX, int dstY,
111
                            int width, int height)
112
{
113
    if (image->getTextureType() == GL_TEXTURE_2D)
114
    {
115
        // Find OpenGL normalized texture coordinates.
116
        float texX1 = srcX / (float) image->getTextureWidth();
117
        float texY1 = srcY / (float) image->getTextureHeight();
118
        float texX2 = (srcX + width) / (float) image->getTextureWidth();
119
        float texY2 = (srcY + height) / (float) image->getTextureHeight();
120
121
        glTexCoord2f(texX1, texY1);
122
        glVertex2i(dstX, dstY);
123
        glTexCoord2f(texX2, texY1);
124
        glVertex2i(dstX + width, dstY);
125
        glTexCoord2f(texX2, texY2);
126
        glVertex2i(dstX + width, dstY + height);
127
        glTexCoord2f(texX1, texY2);
128
        glVertex2i(dstX, dstY + height);
129
    }
130
    else
131
    {
132
        glTexCoord2i(srcX, srcY);
133
        glVertex2i(dstX, dstY);
134
        glTexCoord2i(srcX + width, srcY);
135
        glVertex2i(dstX + width, dstY);
136
        glTexCoord2i(srcX + width, srcY + height);
137
        glVertex2i(dstX + width, dstY + height);
138
        glTexCoord2i(srcX, srcY + height);
139
        glVertex2i(dstX, dstY + height);
140
    }
141
}
142
143
static inline void drawRescaledQuad(Image *image,
144
                            int srcX, int srcY, int dstX, int dstY,
145
                            int width, int height,
146
                            int desiredWidth, int desiredHeight)
147
{
148
    if (image->getTextureType() == GL_TEXTURE_2D)
149
    {
150
        // Find OpenGL normalized texture coordinates.
151
        float texX1 = srcX / (float) image->getTextureWidth();
152
        float texY1 = srcY / (float) image->getTextureHeight();
153
        float texX2 = (srcX + width) / (float) image->getTextureWidth();
154
        float texY2 = (srcY + height) / (float) image->getTextureHeight();
155
156
        glTexCoord2f(texX1, texY1);
157
        glVertex2i(dstX, dstY);
158
        glTexCoord2f(texX2, texY1);
159
        glVertex2i(dstX + desiredWidth, dstY);
160
        glTexCoord2f(texX2, texY2);
161
        glVertex2i(dstX + desiredWidth, dstY + desiredHeight);
162
        glTexCoord2f(texX1, texY2);
163
        glVertex2i(dstX, dstY + desiredHeight);
164
    }
165
    else
166
    {
167
        glTexCoord2i(srcX, srcY);
168
        glVertex2i(dstX, dstY);
169
        glTexCoord2i(srcX + width, srcY);
170
        glVertex2i(dstX + desiredWidth, dstY);
171
        glTexCoord2i(srcX + width, srcY + height);
172
        glVertex2i(dstX + desiredWidth, dstY + desiredHeight);
173
        glTexCoord2i(srcX, srcY + height);
174
        glVertex2i(dstX, dstY + desiredHeight);
175
    }
176
}
177
178
179
bool OpenGLGraphics::drawImage(Image *image, int srcX, int srcY,
180
                               int dstX, int dstY,
181
                               int width, int height, bool useColor)
182
{
183
    if (!image)
184
        return false;
185
186
    srcX += image->mBounds.x;
187
    srcY += image->mBounds.y;
188
189
    if (!useColor)
190
        glColor4f(1.0f, 1.0f, 1.0f, image->mAlpha);
191
192
    glBindTexture(Image::mTextureType, image->mGLImage);
193
194
    setTexturingAndBlending(true);
195
196
    // Draw a textured quad.
197
    glBegin(GL_QUADS);
198
    drawQuad(image, srcX, srcY, dstX, dstY, width, height);
199
    glEnd();
200
201
    if (!useColor)
202
        glColor4ub(mColor.r, mColor.g, mColor.b, mColor.a);
203
204
    return true;
205
}
206
207
bool OpenGLGraphics::drawRescaledImage(Image *image, int srcX, int srcY,
208
                               int dstX, int dstY,
209
                               int width, int height,
210
                               int desiredWidth, int desiredHeight,
211
                               bool useColor)
212
{
213
    return drawRescaledImage(image, srcX, srcY,
214
                             dstX, dstY,
215
                             width, height,
216
                             desiredWidth, desiredHeight,
217
                             useColor, true);
218
}
219
220
bool OpenGLGraphics::drawRescaledImage(Image *image, int srcX, int srcY,
221
                               int dstX, int dstY,
222
                               int width, int height,
223
                               int desiredWidth, int desiredHeight,
224
                               bool useColor, bool smooth)
225
{
226
    if (!image)
227
        return false;
228
229
    // Just draw the image normally when no resizing is necessary,
230
    if (width == desiredWidth && height == desiredHeight)
231
        return drawImage(image, srcX, srcY, dstX, dstY, width, height, useColor);
232
233
    // When the desired image is smaller than the current one,
234
    // disable smooth effect.
235
    if (width > desiredWidth && height > desiredHeight)
236
        smooth = false;
237
238
    srcX += image->mBounds.x;
239
    srcY += image->mBounds.y;
240
241
    if (!useColor)
242
        glColor4f(1.0f, 1.0f, 1.0f, image->mAlpha);
243
244
    glBindTexture(Image::mTextureType, image->mGLImage);
245
246
    setTexturingAndBlending(true);
247
248
    // Draw a textured quad.
249
    glBegin(GL_QUADS);
250
    drawRescaledQuad(image, srcX, srcY, dstX, dstY, width, height,
251
                     desiredWidth, desiredHeight);
252
253
    if (smooth) // A basic smooth effect...
254
    {
255
        glColor4f(1.0f, 1.0f, 1.0f, 0.2f);
256
        drawRescaledQuad(image, srcX, srcY, dstX - 1, dstY - 1, width, height,
257
                        desiredWidth + 1, desiredHeight + 1);
258
        drawRescaledQuad(image, srcX, srcY, dstX + 1, dstY + 1, width, height,
259
                        desiredWidth - 1, desiredHeight - 1);
260
261
        drawRescaledQuad(image, srcX, srcY, dstX + 1, dstY, width, height,
262
                        desiredWidth - 1, desiredHeight);
263
        drawRescaledQuad(image, srcX, srcY, dstX, dstY + 1, width, height,
264
                        desiredWidth, desiredHeight - 1);
265
    }
266
267
    glEnd();
268
269
    if (!useColor)
270
        glColor4ub(mColor.r, mColor.g, mColor.b, mColor.a);
271
272
    return true;
273
}
274
275
/* Optimising the functions that Graphics::drawImagePattern would call,
276
 * so that glBegin...glEnd are outside the main loop. */
277
void OpenGLGraphics::drawImagePattern(Image *image, int x, int y, int w, int h)
278
{
279
    if (!image)
280
        return;
281
282
    const int srcX = image->mBounds.x;
283
    const int srcY = image->mBounds.y;
284
285
    const int iw = image->getWidth();
286
    const int ih = image->getHeight();
287
    if (iw == 0 || ih == 0)
288
        return;
289
290
    glColor4f(1.0f, 1.0f, 1.0f, image->mAlpha);
291
292
    glBindTexture(Image::mTextureType, image->mGLImage);
293
294
    setTexturingAndBlending(true);
295
296
    // Draw a set of textured rectangles
297
    glBegin(GL_QUADS);
298
299
    for (int py = 0; py < h; py += ih)
300
    {
301
        const int height = (py + ih >= h) ? h - py : ih;
302
        const int dstY = y + py;
303
        for (int px = 0; px < w; px += iw)
304
        {
305
            int width = (px + iw >= w) ? w - px : iw;
306
            int dstX = x + px;
307
308
            drawQuad(image, srcX, srcY, dstX, dstY, width, height);
309
        }
310
    }
311
312
    glEnd();
313
314
    glColor4ub(mColor.r, mColor.g, mColor.b, mColor.a);
315
}
316
317
void OpenGLGraphics::drawRescaledImagePattern(Image *image, int x, int y,
318
                                      int w, int h,
319
                                      int scaledWidth, int scaledHeight)
320
{
321
    if (!image)
322
        return;
323
324
    const int srcX = image->mBounds.x;
325
    const int srcY = image->mBounds.y;
326
327
    const int iw = scaledWidth;
328
    const int ih = scaledHeight;
329
    if (iw == 0 || ih == 0)
330
        return;
331
332
    glColor4f(1.0f, 1.0f, 1.0f, image->mAlpha);
333
334
    glBindTexture(Image::mTextureType, image->mGLImage);
335
336
    setTexturingAndBlending(true);
337
338
    // Draw a set of textured rectangles
339
    glBegin(GL_QUADS);
340
341
    for (int py = 0; py < h; py += ih)
342
    {
343
        const int height = (py + ih >= h) ? h - py : ih;
344
        const int dstY = y + py;
345
        for (int px = 0; px < w; px += iw)
346
        {
347
            int width = (px + iw >= w) ? w - px : iw;
348
            int dstX = x + px;
349
350
            drawRescaledQuad(image, srcX, srcY, dstX, dstY,
351
                             width, height, scaledWidth, scaledHeight);
352
        }
353
    }
354
355
    glEnd();
356
357
    glColor4ub(mColor.r, mColor.g, mColor.b, mColor.a);
358
}
359
360
void OpenGLGraphics::updateScreen()
361
{
362
    glFlush();
363
    glFinish();
364
    SDL_GL_SwapBuffers();
365
}
366
367
void OpenGLGraphics::_beginDraw()
368
{
369
    glMatrixMode(GL_TEXTURE);
370
    glLoadIdentity();
371
372
    glMatrixMode(GL_PROJECTION);
373
    glLoadIdentity();
374
375
    glOrtho(0.0, (double)mScreen->w, (double)mScreen->h, 0.0, -1.0, 1.0);
376
377
    glMatrixMode(GL_MODELVIEW);
378
    glLoadIdentity();
379
380
    glEnable(GL_SCISSOR_TEST);
381
382
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
383
384
    pushClipArea(gcn::Rectangle(0, 0, mScreen->w, mScreen->h));
385
}
386
387
void OpenGLGraphics::_endDraw()
388
{
389
}
390
391
SDL_Surface* OpenGLGraphics::getScreenshot()
392
{
393
    int h = mScreen->h;
394
    int w = mScreen->w;
395
396
    SDL_Surface *screenshot = SDL_CreateRGBSurface(
397
            SDL_SWSURFACE,
398
            w, h, 24,
399
            0xff0000, 0x00ff00, 0x0000ff, 0x000000);
400
401
    if (SDL_MUSTLOCK(screenshot))
402
        SDL_LockSurface(screenshot);
403
404
    // Grap the pixel buffer and write it to the SDL surface
405
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
406
    glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, screenshot->pixels);
407
408
    // Flip the screenshot, as OpenGL has 0,0 in bottom left
409
    unsigned int lineSize = 3 * w;
410
    GLubyte* buf = (GLubyte*)malloc(lineSize);
411
412
    for (int i = 0; i < (h / 2); i++)
413
    {
414
        GLubyte *top = (GLubyte*)screenshot->pixels + lineSize * i;
415
        GLubyte *bot = (GLubyte*)screenshot->pixels + lineSize * (h - 1 - i);
416
417
        memcpy(buf, top, lineSize);
418
        memcpy(top, bot, lineSize);
419
        memcpy(bot, buf, lineSize);
420
    }
421
422
    free(buf);
423
424
    if (SDL_MUSTLOCK(screenshot))
425
        SDL_UnlockSurface(screenshot);
426
427
    return screenshot;
428
}
429
430
bool OpenGLGraphics::pushClipArea(gcn::Rectangle area)
431
{
432
    int transX = 0;
433
    int transY = 0;
434
435
    if (!mClipStack.empty())
436
    {
437
        transX = -mClipStack.top().xOffset;
438
        transY = -mClipStack.top().yOffset;
439
    }
440
441
    bool result = gcn::Graphics::pushClipArea(area);
442
443
    transX += mClipStack.top().xOffset;
444
    transY += mClipStack.top().yOffset;
445
446
    glPushMatrix();
447
    glTranslatef(transX, transY, 0);
448
    glScissor(mClipStack.top().x,
449
            mScreen->h - mClipStack.top().y - mClipStack.top().height,
450
            mClipStack.top().width,
451
            mClipStack.top().height);
452
453
    return result;
454
}
455
456
void OpenGLGraphics::popClipArea()
457
{
458
    gcn::Graphics::popClipArea();
459
460
    if (mClipStack.empty())
461
        return;
462
463
    glPopMatrix();
464
    glScissor(mClipStack.top().x,
465
              mScreen->h - mClipStack.top().y - mClipStack.top().height,
466
              mClipStack.top().width,
467
              mClipStack.top().height);
468
}
469
470
void OpenGLGraphics::setColor(const gcn::Color& color)
471
{
472
    mColor = color;
473
    glColor4ub(color.r, color.g, color.b, color.a);
474
475
    mColorAlpha = (color.a != 255);
476
}
477
478
void OpenGLGraphics::drawPoint(int x, int y)
479
{
480
    setTexturingAndBlending(false);
481
482
    glBegin(GL_POINTS);
483
    glVertex2i(x, y);
484
    glEnd();
485
}
486
487
void OpenGLGraphics::drawLine(int x1, int y1, int x2, int y2)
488
{
489
    setTexturingAndBlending(false);
490
491
    glBegin(GL_LINES);
492
    glVertex2f(x1 + 0.5f, y1 + 0.5f);
493
    glVertex2f(x2 + 0.5f, y2 + 0.5f);
494
    glEnd();
495
496
    glBegin(GL_POINTS);
497
    glVertex2f(x2 + 0.5f, y2 + 0.5f);
498
    glEnd();
499
}
500
501
void OpenGLGraphics::drawRectangle(const gcn::Rectangle& rect)
502
{
503
    drawRectangle(rect, false);
504
}
505
506
void OpenGLGraphics::fillRectangle(const gcn::Rectangle& rect)
507
{
508
    drawRectangle(rect, true);
509
}
510
511
void OpenGLGraphics::setTargetPlane(int width, int height)
512
{
513
}
514
515
void OpenGLGraphics::setTexturingAndBlending(bool enable)
516
{
517
    if (enable)
518
    {
519
        if (!mTexture)
520
        {
521
            glEnable(Image::mTextureType);
522
            mTexture = true;
523
        }
524
525
        if (!mAlpha)
526
        {
527
            glEnable(GL_BLEND);
528
            mAlpha = true;
529
        }
530
    }
531
    else
532
    {
533
        if (mAlpha && !mColorAlpha)
534
        {
535
            glDisable(GL_BLEND);
536
            mAlpha = false;
537
        }
538
        else if (!mAlpha && mColorAlpha)
539
        {
540
            glEnable(GL_BLEND);
541
            mAlpha = true;
542
        }
543
544
        if (mTexture)
545
        {
546
            glDisable(Image::mTextureType);
547
            mTexture = false;
548
        }
549
    }
550
}
551
552
void OpenGLGraphics::drawRectangle(const gcn::Rectangle& rect, bool filled)
553
{
554
    const float offset = filled ? 0 : 0.5f;
555
556
    setTexturingAndBlending(false);
557
558
    glBegin(filled ? GL_QUADS : GL_LINE_LOOP);
559
    glVertex2f(rect.x + offset, rect.y + offset);
560
    glVertex2f(rect.x + rect.width - offset, rect.y + offset);
561
    glVertex2f(rect.x + rect.width - offset, rect.y + rect.height - offset);
562
    glVertex2f(rect.x + offset, rect.y + rect.height - offset);
563
    glEnd();
564
}
565
566
#endif // USE_OPENGL