view efu.c @ 96:6bf5220fa47e

Urgh .. use memset to silence some bogus GCC warnings about using potentially uninitialized values, while that will not actually be possible. In any case, it is annoying.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 02 Oct 2012 18:52:28 +0300
parents c6cdaa675801
children 93fbce0e6591
line wrap: on
line source

#include "dmlib.h"
#include "dmargs.h"
#include "dmvecmat.h"
#include "dmimage.h"
#include "dmtext.h"
#include <math.h>

#define DM_COLORS (256)

char *optFontFile = "font.ttf",
     *optBitmapFilename = "tnsp.png";
BOOL optBenchmark = FALSE;
int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE;
int optScrWidth = 640, optScrHeight = 480, optFontSize = 20, optScrDepth = 32;
int optBenchmarkLen = 20;

DMOptArg optList[] = {
    { 0, '?', "help",       "Show this help", OPT_NONE },
    { 2, 'v', "verbose",    "Be more verbose", OPT_NONE },
    { 3, 'f', "full",       "Fullscreen", OPT_NONE },
    { 4, 'h', "hw",         "Use SDL hardware surface", OPT_NONE },
    { 5, 's', "size",       "Initial window size/resolution -s 640x480", OPT_ARGREQ },
    { 6, 'd', "depth",      "Color depth of mode/window in bits (8/15/16/32)", OPT_ARGREQ },
    { 7, 'b', "bench",      "Run in benchmark mode", OPT_NONE },
};

const int optListN = sizeof(optList) / sizeof(optList[0]);


void argShowHelp()
{
    dmArgsPrintHelp(stdout, optList, optListN);
}


BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
{
    switch (optN) {
    case 0:
        argShowHelp();
        exit(0);
        break;

    case 2:
        dmVerbosity++;
        break;
    
    case 3:
        optVFlags |= SDL_FULLSCREEN;
        break;

    case 6:
        if (optArg)
            optScrDepth = atoi(optArg);
        break;

    case 5:
        {
            int w, h;
            if (sscanf(optArg, "%dx%d", &w, &h) == 2)
            {
                if (w < 320 || h < 200 || w > 3200 || h > 3200)
                {
                    dmError("Invalid width or height: %d x %d\n", w, h);
                    return FALSE;
                }
                optScrWidth = w;
                optScrHeight = h;
            }
            else 
            {
                dmError("Invalid size argument '%s'.\n", optArg);
                return FALSE;
            }
        }
        break;

    case 7:
        optBenchmark = TRUE;
        break;

    default:
        dmError("Unknown option '%s'.\n", currArg);
        return FALSE;
    }
    
    return TRUE;
}


void DM_MakePalette(SDL_Surface *scr)
{
    SDL_Color pal[DM_COLORS];
    int n;

    for (n = 0; n < 256; n++)
    {
        pal[n].r = n;
        pal[n].g = n;
        pal[n].b = n;
    }

    SDL_SetColors(scr, pal, 0, DM_COLORS);
}


void DM_PrintRect(FILE *f, SDL_Rect *r)
{
    fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n",
        r->x, r->y, r->w, r->h);
}

BOOL DM_InitializeVideo(SDL_Surface **screen)
{
    *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE);
    if (*screen == NULL)
    {
        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
        return FALSE;
    }

#if 0
    SDL_Rect r;
    r.x = -50;
    r.y = 50;
    r.w = 700;
    r.h = 300;
    DM_PrintRect(stderr, &r);
    SDL_SetClipRect(*screen, &r);
    DM_PrintRect(stderr, &r);
    DM_PrintRect(stderr, &((*screen)->clip_rect));
#endif

    return TRUE;
}

void DM_Random(SDL_Surface *screen, int q)
{
    Uint8 *pix = screen->pixels;
    int xc, yc;
    
    for (yc = 0; yc < screen->h; yc++)
    {
        Uint8 *dp = pix;

        for (xc = 0; xc < screen->w; xc++)
            *dp++ = yc + (xc ^ q) + (yc & q);

        pix += screen->pitch;
    }
}

void DM_Perlin(SDL_Surface *screen, float f)
{
    Uint8 *pix = screen->pixels;
    int xc, yc;

    for (yc = 0; yc < screen->h; yc++)
    {
        Uint8 *dp = pix;

        for (xc = 0; xc < screen->w; xc++)
        {
            *dp++ = 128 + dmPerlinNoise2D(xc, yc, 0.01, 0.1, 3) / 34.0;
        }

        pix += screen->pitch;
    }
}


#define QWIDTH	256
#define QHEIGHT	160

typedef Uint8 DMBlockMap[QHEIGHT][QWIDTH];


static DMFloat dmClip(DMFloat a)
{
    return (a < 0.0f ? 0.0f : (a > 1.0f ? 1.0f : a));
}


void dmMakeBumpMap(DMBlockMap map, DMFloat q, DMFloat m)
{
    int x, y;
    for (y = 0; y < QHEIGHT; y++)
        for (x = 0; x < QWIDTH; x++)
        {
            DMFloat f = 0.40f + dmPerlinNoise2D(x, y, 1.1f, q, 2);
            map[y][x] = (int) (dmClip(f) * m);
        }
}


void dmShadowTraceHeightMap(DMBlockMap lightMap, DMBlockMap pheightMap, DMVector *light)
{
    int i, j;

    light->z = 150;

    for (j = 0; j < QHEIGHT; j++)
        for (i = 0; i < QWIDTH; i++)
        {
            DMVector vr, vl, va;
            DMFloat vrayLen, vfactor;
            int vlen;
            BOOL wasHit;

            // Perform shadow occlusion via simplistic raytracing
            vr.x = i;
            vr.y = j;
            vr.z = 200; //light->z; // - 10.0;
            
            // Calculate light vector vector
            dm_vector_sub_r(&vl, &vr, light);
            vrayLen = dm_vector_length(&vl);
            
#if 1
            dm_vector_copy(&va, &vl);
            dm_vector_normalize(&va);
            dm_vector_copy(&vr, light);

            vlen = 0;
            wasHit = FALSE;
            do
            {
                float h;
                
                // If ray is inside the heightmap, get value
                if (vr.x >= 0 && vr.y >= 0 && vr.x < QWIDTH && vr.y < QHEIGHT)
                    h = pheightMap[(int) vr.y][(int) vr.x];
                else
                    break;
                
                // Check for hits
                if (h > vr.z)
                    wasHit = TRUE;
                else
                {
                    // Move forwards
                    dm_vector_add(&vr, &va);
                    vlen++;
                }
            }
            while (!wasHit && vlen <= vrayLen);

            // Check if the ray hit something, e.g. is this point occluded?
            if (wasHit && vlen < vrayLen)
            {
                vfactor = vlen * 0.05;
            }
            else
                vfactor = vlen * 0.001;
#endif

#if 0
            {
                /* Calculate light's intensity based on the angle it "hits"
                 *
                 * 1) Calculate the vectors that form the imaginary "plane"
                 * 2) Cross-product -> normal vector of the plane
                 * 2) Normalize the normal vector
                 * 3) Calculate light vector's hit angle by dot product
                 */
                DMVector v1, v2;
                DMFloat c;

                v1.x = 2.0f;
                v1.y = 0.0f;
                v1.z = (DMFloat) (pheightMap[j][i] - pheightMap[j][i + 1]);

                v2.x = 0.0f;
                v2.y = 2.0f;
                v2.z = (DMFloat) (pheightMap[j][i] - pheightMap[j + 1][i]);

                dm_vector_cross(&vr, &v1, &v2);
                dm_vector_normalize(&vr);
                dm_vector_normalize(&vl);
                c = dm_vector_dot(&vl, &vr);

		vrayLen = 255 - (vrayLen * 0.1) * vrayLen + (c * 128.0f) + (vfactor * vfactor * 1255);
            }
#else
            vrayLen = 255 - vrayLen * vrayLen * (vfactor * vfactor);
            if (vrayLen < 0) vrayLen = 0;
            vrayLen -= pheightMap[j][i];
#endif

            // Clip result
            if (vrayLen < 0)
                vrayLen = 0;
            else if (vrayLen > 255.0f)
                vrayLen = 255.0f;

            lightMap[j][i] = vrayLen;
        }
}


int main(int argc, char *argv[])
{
    SDL_Surface *screen = NULL, *bmap = NULL, *logo = NULL;
    TTF_Font *font = NULL;
    SDL_Color fontcol={255,155,155,0};
    SDL_Event event;
    int mouseX, mouseY;
    BOOL initSDL = FALSE, initTTF = FALSE, exitFlag, showMap = FALSE;
    
    if (!dmArgsProcess(argc, argv, optList, optListN,
        argHandleOpt, NULL, FALSE))
        exit(1);

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
    {
        dmError("Could not initialize SDL: %s\n", SDL_GetError());
        goto error_exit;
    }
    initSDL = TRUE;


    if (TTF_Init() < 0)
    {
        dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError());
        goto error_exit;
    }
    initTTF = TRUE;

    font = TTF_OpenFont(optFontFile, optFontSize);
    if (font == NULL)
    {
        dmError("Could not load TTF font '%s' (%d): %s\n",
            optFontFile, optFontSize, SDL_GetError());
        goto error_exit;
    }
    TTF_SetFontStyle(font, TTF_STYLE_NORMAL);


    DMResource *res = dmf_create_stdio(optBitmapFilename, "rb");
    if (res == NULL)
    {
        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
        goto error_exit;
    }
    logo = dmLoadImage(res);
    dmf_close(res);
    if (logo == NULL)
    {
        dmError("Could not load image file '%s'.\n", optBitmapFilename);
        goto error_exit;
    }


    if (optBenchmark)
    {
        screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0);
        if (screen == NULL)
        {
            dmError("Could not create screen surface.\n");
            goto error_exit;
        }
        
        dmMsg(0, "Benchmark mode, not opening window.\n");
    }
    else
    {
        if (!DM_InitializeVideo(&screen))
            goto error_exit;

        SDL_WM_SetCaption("Halleluja", "DMT");
    }

    dmPerlinInit();

    bmap = SDL_CreateRGBSurface(SDL_SWSURFACE, QWIDTH, QHEIGHT, 8, 0, 0, 0, 0);
    DM_MakePalette(bmap);
    DM_Random(bmap, 15);
    
    DMVector light;
    DMBlockMap heightMap;
    light.x = light.y = 128;
    light.z = 128;
    dmMakeBumpMap(heightMap, 0.06, 254);



    int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0;
    exitFlag = FALSE;

    if (optBenchmark)
        dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen);

    while (!exitFlag)
    {
        if (!optBenchmark)
        {
            while (SDL_PollEvent(&event))
            switch (event.type)
            {
                case SDL_KEYDOWN:
                    switch (event.key.keysym.sym)
                    {
                        case SDLK_ESCAPE: exitFlag = TRUE; break;
                        
                        case SDLK_F1:
                            DM_Random(bmap, (SDL_GetTicks() / 10) % 1000);
                            break;

                        case SDLK_F2:
                            DM_Perlin(bmap, SDL_GetTicks() / 100);
                            break;

                        case SDLK_F5:
                            showMap = !showMap;
                            break;
                            
                        default:
                            break;
                    }

                    break;
                
                case SDL_VIDEORESIZE:
                    optScrWidth = event.resize.w;
                    optScrHeight = event.resize.h;

                    if (!DM_InitializeVideo(&screen))
                        goto error_exit;

                    break;
                
                case SDL_VIDEOEXPOSE:
                    break;

                case SDL_QUIT:
                    exit(0);
            }

            SDL_GetMouseState(&mouseX, &mouseY);
            light.x = ((DMFloat) mouseX * QWIDTH) / (DMFloat) optScrWidth;
            light.y = ((DMFloat) mouseY * QHEIGHT) / (DMFloat) optScrHeight;
        }

        if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0)
        {
            dmError("Can't lock surface.\n");
            goto error_exit;
        }


        if (showMap)
            memcpy(bmap->pixels, heightMap, QWIDTH * QHEIGHT);
        else
            dmShadowTraceHeightMap(bmap->pixels, logo->pixels, &light);
        
        dmScaledBlitSurfaceAny(bmap, 0, 0, screen->w, screen->h, screen, DMD_NONE);



        if (!optBenchmark)
        {
            dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", 
                (float) (numFrames * 1000.0f) / (float) (endTime - startTime));

            if (SDL_MUSTLOCK(screen) != 0)
                SDL_UnlockSurface(screen);

            SDL_Flip(screen);
            SDL_Delay(10);
        }

        endTime = SDL_GetTicks();
        numFrames++;

        if (optBenchmark)
        {
            if (endTime - startTime > optBenchmarkLen * 1000)
                exitFlag = TRUE;
        }
    }

    // Print benchmark results
    dmMsg(0, "%d frames in %d ms, fps = %1.3f\n",
        numFrames, endTime - startTime,
        (float) (numFrames * 1000.0f) / (float) (endTime - startTime));


error_exit:
    dmMsg(0, "Shutting down dmlib.\n");
    if (screen)
        SDL_FreeSurface(screen);

    if (logo)
        SDL_FreeSurface(logo);

    if (font)
        TTF_CloseFont(font);

    if (initSDL)
        SDL_Quit();

    if (initTTF)
        TTF_Quit();

    return 0;
}