view vptest.c @ 238:9643c517967d

Beginnings of a lines + sprites "3D" "model" renderer.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 09 Oct 2012 06:06:08 +0300
parents 79dac918c81e
children 61b9cd67cd55
line wrap: on
line source

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

#define DM_COLORS (256)

char *optFontFile = "font.ttf",
     *optBitmapFilename = "map.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 = 320;
    r.h = 200;
    DM_PrintRect(stderr, &r);
    SDL_SetClipRect(*screen, &r);
    DM_PrintRect(stderr, &r);
    DM_PrintRect(stderr, &((*screen)->clip_rect));
#endif

    return TRUE;
}


#define DM_DRAWLINE_NAME dmDrawLineSpec
#define DM_DRAWLINE_DST_BYTES 4
#define DM_DRAWLINE_DST_TYPE DMRGBA32
#define DM_DRAWLINE_ARGS , SDL_Surface *bmp
#define DM_DRAWLINE_INNER \
     dmUnscaledBlitSurface32to32Transparent(bmp, x0, y0, screen);
#define DM_DRAWLINE_SPEC
#include "dmdrawline.h"


typedef struct
{
    int v1, v2, type;
} DM3DLine;

typedef struct
{
    int v, bitmap;
} DM3DSprite;

typedef struct
{
    char name[64];
    SDL_Surface *img;
} DM3DBitmap;

typedef struct
{
    int nvertices, nvertexalloc;
    DMVector *vertices;

    int nlines, nlinesalloc;
    DM3DLine *lines;

    int nbitmaps, nbitmapssalloc;
    DM3DBitmap *bitmaps;
    
    int nsprites, nspritesalloc;
    DM3DSprite *sprites;
} DM3DLineSpriteModel;


int dmFind3DSurface(int nitems, DM3DBitmap *items, DM3DBitmap *item)
{
    int i;
    for (i = 0; i < nitems; i++)
    if (strcmp(item->name, items[i].name) == 0)
        return i;
    return -1;
}

#define DM_FIND_ITEM_FUNC(NAME, TYPE) \
int dmFind3D ## NAME (int nitems, TYPE *items, TYPE *item) \
{ \
    int i; \
    for (i = 0; i < nitems; i++) \
    if (memcmp(item, items+i, sizeof(TYPE)) == 0) \
        return i; \
    return -1; \
}

#define DM_ADD_ITEM_FUNC(NAME, TYPE) \
int dmAdd3D ## NAME (int *nitems, int *nalloc, TYPE **items, TYPE *item, int *res) \
{ \
    int tmp; \
    if ((tmp = dmFind3D ## NAME (*nitems, *items, item)) >= 0) \
    { \
        if (res != NULL) *res = tmp; \
        return DMERR_OK; \
    } \
    if ((*nitems) + 1 >= *nalloc) \
    { \
        (*nalloc) += 16; \
        if ((*items = dmRealloc(*items, *nalloc * sizeof(TYPE))) == NULL) \
            return DMERR_MALLOC; \
    } \
    memcpy((*items) + (*nitems), item, sizeof(TYPE)); \
    if (res != NULL) *res = *nitems; \
    (*nitems)++; \
    return DMERR_OK; \
}

DM_FIND_ITEM_FUNC(Vertex, DMVector)
DM_FIND_ITEM_FUNC(Line, DM3DLine)
DM_FIND_ITEM_FUNC(Sprite, DM3DSprite)

DM_ADD_ITEM_FUNC(Vertex, DMVector)
DM_ADD_ITEM_FUNC(Line, DM3DLine)
DM_ADD_ITEM_FUNC(Sprite, DM3DSprite)
DM_ADD_ITEM_FUNC(Surface, DM3DBitmap)

char *dmSkipWhitespace(char *line, BOOL invert)
{
    if (invert)
        for (; *line && !isspace(*line); line++);
    else
        for (; *line && isspace(*line); line++);
    return line;
}

char *dmSkipUntil(char *line, char ch)
{
    for (; *line && *line != ch; line++);
    return line;
}

BOOL dmReadCoordinate(char **line, float *value, BOOL next)
{
    *line = dmSkipWhitespace(*line, FALSE);
    if (sscanf(*line, "%f", value) != 1) return FALSE;
    if (next)
    {
        *line = dmSkipUntil(*line, ',');
        if (**line != ',') return FALSE;
        *(*line)++;
    }
    else
        *line = dmSkipWhitespace(*line, TRUE);

    return TRUE;
}

BOOL dmReadLineSegments(char *line, DM3DLineSpriteModel *model)
{
    int nvertices, vertex, type;
    int *indices = NULL;
    if (sscanf(line+1, "%d", &nvertices) != 1)
    {
        dmError("No # of segments @ '%s'\n", line);
        goto error;
    }
    
    if ((indices = dmMalloc(sizeof(int) * (nvertices+1))) == NULL)
        goto error;
    
    line = dmSkipWhitespace(line, TRUE);
    for (vertex = 0; vertex <= nvertices; vertex++)
    {
        DMVector p;
        if (!dmReadCoordinate(&line, &p.x, TRUE)) return FALSE;
        if (!dmReadCoordinate(&line, &p.y, TRUE)) return FALSE;
        if (!dmReadCoordinate(&line, &p.z, FALSE)) return FALSE;
        if (dmAdd3DVertex(&model->nvertices, &model->nvertexalloc,
            &model->vertices, &p, &indices[vertex]) != DMERR_OK)
            goto error;
        line = dmSkipWhitespace(line, FALSE);
    }
    
    if (sscanf(line, "%d", &type) != 1)
    {
        dmError("No line type @ '%s'\n", line);
        goto error;
    }
    
    for (vertex = 1; vertex <= nvertices; vertex++)
    {
        DM3DLine line;
        line.v1 = indices[vertex - 1];
        line.v2 = indices[vertex];
        line.type = type;

        if (dmAdd3DLine(&model->nlines, &model->nlinesalloc,
            &model->lines, &line, NULL) != DMERR_OK)
            goto error;
    }

    return TRUE;
error:
    dmFree(indices);
    return FALSE;
}

BOOL dmReadSprite(char *line, DM3DLineSpriteModel *model)
{
    DMVector pt;
    DM3DSprite spr;
    DM3DBitmap bmp, *rbmp;

    line++;
    if (!dmReadCoordinate(&line, &pt.x, TRUE)) return FALSE;
    if (!dmReadCoordinate(&line, &pt.y, TRUE)) return FALSE;
    if (!dmReadCoordinate(&line, &pt.z, FALSE)) return FALSE;
    line = dmSkipWhitespace(line, FALSE);

    if (dmAdd3DVertex(&model->nvertices, &model->nvertexalloc,
        &model->vertices, &pt, &spr.v) != DMERR_OK)
        return FALSE;

    strncpy(bmp.name, line, sizeof(bmp.name));
    bmp.img = NULL;
    if (dmAdd3DSurface(&model->nbitmaps, &model->nbitmapssalloc,
        &model->bitmaps, &bmp, &spr.bitmap) != DMERR_OK)
        return FALSE;
    
    rbmp = &(model->bitmaps[spr.bitmap]);
    if (rbmp->img == NULL)
    {
        DMResource *res = dmf_create_stdio(rbmp->name, "rb");
        if (res == NULL)
        {
            dmError("Could not open resource file '%s'.\n", rbmp->name);
            return FALSE;
        }
        rbmp->img = dmLoadImage(res);
        dmf_close(res);
        if (rbmp->img == NULL)
        {
            dmError("Could not load image file '%s'.\n", rbmp->name);
            return FALSE;
        }
    }

    if (dmAdd3DSprite(&model->nsprites, &model->nspritesalloc,
        &model->sprites, &spr, NULL) != DMERR_OK)
        return FALSE;

    return TRUE;
}

int dmRead3DLineSpriteModel(DMResource *f, DM3DLineSpriteModel *model)
{
    char line[512];

    memset(model, 0, sizeof(*model));

    while (dmfgets(line, sizeof(line), f) != NULL)
    {
        char *start = dmSkipWhitespace(line, FALSE);
        switch (*start)
        {
            case 0:
            case '#':
                break;
            
            case 'L':
                if (!dmReadLineSegments(start, model))
                    return DMERR_INVALID_DATA;
                break;
            
            case 'S':
                if (!dmReadSprite(start, model))
                    return DMERR_INVALID_DATA;
                break;
            
            default:
                printf("'%s'\n", start);
                break;
        }
    }

    return DMERR_OK;
}


void dmDraw3DLineSpriteModel(SDL_Surface *screen, DM3DLineSpriteModel *model, DMVector pos, SDL_Surface *fbmap, Uint32 lcol)
{
    int i;
    for (i = 0; i < model->nlines; i++)
    {
        DM3DLine *line = &model->lines[i];
        DMVector *v1 = &model->vertices[line->v1],
                 *v2 = &model->vertices[line->v2];
        if (line->type > 1)
            dmDrawLineSpec(screen, pos.x + v1->x, pos.y + v1->y, pos.x + v2->x, pos.y + v2->y, lcol, fbmap);
        else
            dmDrawLine32(screen, pos.x + v1->x, pos.y + v1->y, pos.x + v2->x, pos.y + v2->y, lcol);
    }

    for (i = 0; i < model->nsprites; i++)
    {
        DM3DSprite *sprite = &model->sprites[i];
        DM3DBitmap *bmp = &model->bitmaps[sprite->bitmap];
        DMVector *v = &model->vertices[sprite->v];

        dmUnscaledBlitSurface32to32Transparent(
            bmp->img,
            pos.x + v->x, pos.y + v->y, screen);
    }
}


int main(int argc, char *argv[])
{
    SDL_Surface *screen = NULL, *bmap = NULL, *fbmap = NULL;
    SDL_Color fontcol = { 255, 155, 155, 0 };
    SDL_Event event;
    TTF_Font *font = NULL;
    int mouseX, mouseY, bx, by;
    BOOL initSDL = FALSE, initTTF = FALSE, exitFlag;
    DM3DLineSpriteModel model;
    
    dmVerbosity = 5;
    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;
    }
    bmap = dmLoadImage(res);
    dmf_close(res);
    if (bmap == NULL)
    {
        dmError("Could not load image file '%s'.\n", optBitmapFilename);
        goto error_exit;
    }

    res = dmf_create_stdio("trans6x6.png", "rb");
    if (res == NULL)
    {
        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
        goto error_exit;
    }
    fbmap = dmLoadImage(res);
    dmf_close(res);
    if (fbmap == NULL)
    {
        dmError("Could not load image file '%s'.\n", optBitmapFilename);
        goto error_exit;
    }
    
    
    res = dmf_create_stdio("mole.3d", "r");
    if (res == NULL)
    {
        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
        goto error_exit;
    }
    dmRead3DLineSpriteModel(res, &model);
    dmf_close(res);
    
    
    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");
    }

    Uint32 lcol = dmMapRGB(screen, 255,255,255);

    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;
                            
                        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);
            bx = 300 - ((DMFloat) mouseX * 500.0f ) / (DMFloat) optScrWidth;
            by = 300 - ((DMFloat) mouseY * 500.0f ) / (DMFloat) optScrHeight;
        }
        else
        {
            bx = 0;
            by = 0;
        }

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


        dmClearSurface(screen, 0);

        float f = SDL_GetTicks() / 150.0f,
              qw = (float) 32.0 * (1.0 + sin(f) * 0.1),
              qh = (float) 32.0 * (1.0 + sin(f) * 0.1),
              qw2 = (float) 132.0 * (1.0 + sin(f) * 0.1),
              qh2 = (float) 132.0 * (1.0 + sin(f) * 0.1);

        dmScaledBlitSurfaceAny(bmap, bx-qw2, by-qh2, bmap->w+qw2, bmap->h+qh2, screen,
//        DMD_NONE
        DMD_SATURATE
        );

        dmScaledBlitSurface32to32TransparentGA(bmap, bx*2-qw, by*2-qh, bmap->w+qw, bmap->h+qh, screen,
            128 + sin(f*0.1) * 120.0f);


        DMVector pos;
        pos.x = 10;
        pos.y = 10;
        pos.z = 0;
        
        dmDraw3DLineSpriteModel(screen, &model, pos, fbmap, lcol);


        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(25);
        }

        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 (bmap)
        SDL_FreeSurface(bmap);

    if (font)
        TTF_CloseFont(font);

    if (initSDL)
        SDL_Quit();

    if (initTTF)
        TTF_Quit();

    return 0;
}