view dmq3d.c @ 510:43ea59887c69

Start work on making C64 formats encoding possible by changing DMDecodeOps to DMEncDecOps and adding fields and op enums for custom encode functions, renaming, etc. Split generic op sanity checking into a separate function in preparation for its use in generic encoding function.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 19 Nov 2012 15:06:01 +0200
parents d5a90ba09dc4
children 3d813c81f33c
line wrap: on
line source

#include "dmlib.h"
#include "dmq3d.h"
#include "dmimage.h"


#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_INIT const int px = bmp->w / 2, py = bmp->h / 2;
#define DM_DRAWLINE_INNER \
     dmUnscaledBlitSurface32to32Transparent(bmp, x0-px, y0-py, screen);
#define DM_DRAWLINE_SPEC
#include "dmdrawline.h"


static int dmFind3DBitmap(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) \
static 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) \
static 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) \
        { \
            dmError("Error allocating memory for " # TYPE ".\n"); \
            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(Vector, DM3DVector)
DM_FIND_ITEM_FUNC(Sprite, DM3DSprite)

DM_ADD_ITEM_FUNC(Vertex, DMVector)
DM_ADD_ITEM_FUNC(Vector, DM3DVector)
DM_ADD_ITEM_FUNC(Sprite, DM3DSprite)
DM_ADD_ITEM_FUNC(Bitmap, DM3DBitmap)


int dmAdd3DVectorSpriteModelVertex(DM3DVectorSpriteModel *model, DMVector *v, int *index)
{
    return dmAdd3DVertex(&model->nvertices, &model->nvertexalloc,
         &model->vertices, v, index);
}

int dmAdd3DVectorSpriteModelVector(DM3DVectorSpriteModel *model, DM3DVector *v, int *index)
{
    return dmAdd3DVector(&model->nlines, &model->nlinesalloc,
         &model->lines, v, index);
}

int dmAdd3DVectorSpriteModelSprite(DM3DVectorSpriteModel *model, DM3DSprite *v, int *index)
{
    return dmAdd3DSprite(&model->nsprites, &model->nspritesalloc,
         &model->sprites, v, index);
}

int dmAdd3DVectorSpriteModelBitmap(DM3DVectorSpriteModel *model, DM3DBitmap *v, int *index)
{
    return dmAdd3DBitmap(&model->nbitmaps, &model->nbitmapsalloc,
         &model->bitmaps, v, index);
}


static inline DMFloat dmPX(int cx, DMVector p)
{
    return cx + (p.x * 250.0f) / p.z;
}


static inline DMFloat dmPY(int cy, DMVector p)
{
    return cy + (p.y * 250.0f) / p.z;
}


void dmDraw3DVectorSpriteModel(SDL_Surface *screen, const DM3DVectorSpriteModel *model, const DMVector *pos, const DMMatrix *mat, SDL_Surface *fbmap, const Uint32 lcol)
{
    int i;
    int cx = screen->w / 2, cy = screen->h / 2;
    for (i = 0; i < model->nlines; i++)
    {
        DM3DVector *line = &model->lines[i];
        DMVector pv[2];
        dm_vector_copy(&pv[0], &model->vertices[line->v1]);
        dm_vector_copy(&pv[1], &model->vertices[line->v2]);
        dm_vector_mul_by_mat_n(pv, 2, mat);
        dm_vector_add(&pv[0], pos);
        dm_vector_add(&pv[1], pos);
        
        if (pv[0].z <= 0 && pv[1].z <= 0)
            continue;

        if (line->type > 1)
            dmDrawLineSpec(screen, dmPX(cx, pv[0]), dmPY(cy, pv[0]), dmPX(cx, pv[1]), dmPY(cy, pv[1]), lcol, fbmap);
        else
            dmDrawLine32(screen, dmPX(cx, pv[0]), dmPY(cy, pv[0]), dmPX(cx, pv[1]), dmPY(cy, pv[1]), lcol);
    }

    for (i = 0; i < model->nsprites; i++)
    {
        DM3DSprite *sprite = &model->sprites[i];
        DM3DBitmap *bmp = &model->bitmaps[sprite->bitmap];
        DMVector pv;
        dm_vector_mul_by_mat(&pv, &model->vertices[sprite->v], mat);
        dm_vector_add(&pv, pos);
        if (pv.z <= 0)
            continue;
        dmUnscaledBlitSurface32to32Transparent(bmp->img, dmPX(cx, pv), dmPY(cy, pv), screen);
    }
}


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


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


static BOOL dmReadCoordinate(const char *orig, char **line, float *value, BOOL next)
{
    *line = dmSkipWhitespace(*line, FALSE);
    if (sscanf(*line, "%f", value) != 1)
    {
        dmError("Expected floating point value @ %d:\n%s\n", (*line - orig), orig);
        return FALSE;
    }
    if (next)
    {
        *line = dmSkipUntil(*line, ',');
        if (**line != ',')
        {
            dmError("Expected comma @ %d:\n%s\n", (*line - orig), orig);
            return FALSE;
        }
        *(*line)++;
    }
    else
        *line = dmSkipWhitespace(*line, TRUE);

    return TRUE;
}


static BOOL dmReadVectorSegments(char *line, DM3DVectorSpriteModel *model, BOOL relative, const DMVector *pt)
{
    DMVector v, p, *t;
    int nvertices, vertex, type;
    int *indices = NULL;
    char *ptr = line;
    
    if (sscanf(ptr+1, "%d", &nvertices) != 1)
    {
        dmError("No # of segments @ '%s'\n", ptr);
        goto error;
    }
    
    if ((indices = dmMalloc(sizeof(int) * (nvertices+1))) == NULL)
        goto error;
    
    ptr = dmSkipWhitespace(ptr, TRUE);
    dm_vector_copy(&v, pt);
    for (vertex = 0; vertex <= nvertices; vertex++)
    {
        if (*ptr == 'Z')
        {
            indices[vertex] = indices[0];
            ptr++;
        }
        else
        {
            if (!dmReadCoordinate(line, &ptr, &p.x, TRUE)) return FALSE;
            if (!dmReadCoordinate(line, &ptr, &p.y, TRUE)) return FALSE;
            if (!dmReadCoordinate(line, &ptr, &p.z, FALSE)) return FALSE;
            if (relative)
            {
                dm_vector_add(&v, &p);
                t = &v;
            }
            else
            {
                dm_vector_add_r(&v, &p, pt);
                t = &v;
            }

            if (dmAdd3DVectorSpriteModelVertex(model, t, &indices[vertex]) != DMERR_OK)
                goto error;
        }

        ptr = dmSkipWhitespace(ptr, FALSE);
    }
    
    if (sscanf(ptr, "%d", &type) != 1)
    {
        dmError("No line type @ '%s'\n", ptr);
        goto error;
    }
    
    for (vertex = 1; vertex <= nvertices; vertex++)
    {
        DM3DVector vec;
        vec.v1 = indices[vertex - 1];
        vec.v2 = indices[vertex];
        vec.type = type;
        if (dmAdd3DVectorSpriteModelVector(model, &vec, NULL) != DMERR_OK)
            goto error;
    }

    return TRUE;

error:
    dmFree(indices);
    return FALSE;
}


static BOOL dmReadSprite(char *line, DM3DVectorSpriteModel *model, DMVector *pos)
{
    DMVector pt;
    DM3DSprite spr;
    char *ptr = line;

    ptr++;
    if (!dmReadCoordinate(line, &ptr, &pt.x, TRUE)) return FALSE;
    if (!dmReadCoordinate(line, &ptr, &pt.y, TRUE)) return FALSE;
    if (!dmReadCoordinate(line, &ptr, &pt.z, FALSE)) return FALSE;
    ptr = dmSkipWhitespace(ptr, FALSE);
    if (*ptr != 'B')
    {
        dmError("No bitmap definition found for sprite.\n");
        return FALSE;
    }

    spr.bitmap = atoi(ptr + 1);

    dm_vector_add(&pt, pos);
    if (dmAdd3DVectorSpriteModelVertex(model, &pt, &spr.v) != DMERR_OK)
        return FALSE;

    if (dmAdd3DVectorSpriteModelSprite(model, &spr, NULL) != DMERR_OK)
        return FALSE;

    return TRUE;
}

static BOOL dmReadBitmap(char *line, DM3DVectorSpriteModel *model, DMResourceLib *lib)
{
    DM3DBitmap bmp, *rbmp;
    int index;
    char *ptr = line;

    strncpy(bmp.name, ptr + 1, sizeof(bmp.name));
    bmp.img = NULL;

    if (dmAdd3DVectorSpriteModelBitmap(model, &bmp, &index) != DMERR_OK)
        return FALSE;

    rbmp = &(model->bitmaps[index]);
    if (rbmp->img == NULL)
    {
        DMResource *res = dmf_open(lib, rbmp->name);
        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;
        }
    }

    return TRUE;
}


static int dmDoRead3DVectorSpriteModel(DMResource *f, DM3DVectorSpriteModel *model, DMVector *pos)
{
    char line[8192];

    while (dmfgets(line, sizeof(line), f) != NULL)
    {
        char *start = dmSkipWhitespace(line, FALSE);
        switch (*start)
        {
            case 0:
            case '#':
                break;
            
            case 'G':
                {
                int res;
                DMVector pt;
                start++;
                if (!dmReadCoordinate(line, &start, &pt.x, TRUE)) return DMERR_INVALID_DATA;
                if (!dmReadCoordinate(line, &start, &pt.y, TRUE)) return DMERR_INVALID_DATA;
                if (!dmReadCoordinate(line, &start, &pt.z, FALSE)) return DMERR_INVALID_DATA;
                dm_vector_add_r(&pt, pos, &pt);
                if ((res = dmDoRead3DVectorSpriteModel(f, model, &pt)) != DMERR_OK)
                    return res;
                }
                break;
            
            case 'E':
                return DMERR_OK;

            case 'B':
                if (!dmReadBitmap(start, model, f->lib))
                    return DMERR_INVALID_DATA;
                break;

            case 'L':
                if (!dmReadVectorSegments(start, model, FALSE, pos))
                    return DMERR_INVALID_DATA;
                break;

            case 'R':
                if (!dmReadVectorSegments(start, model, TRUE, pos))
                    return DMERR_INVALID_DATA;
                break;
            
            case 'S':
                if (!dmReadSprite(start, model, pos))
                    return DMERR_INVALID_DATA;
                break;
            
            default:
                break;
        }
    }
    return DMERR_OK;
}


int dmRead3DVectorSpriteModel(DMResource *f, DM3DVectorSpriteModel **model)
{
    DMVector pos;

    if ((*model = dmMalloc0(sizeof(DM3DVectorSpriteModel))) == NULL)
        return DMERR_MALLOC;

    memset(&pos, 0, sizeof(pos));
    
    return dmDoRead3DVectorSpriteModel(f, *model, &pos);
}


void dmFree3DVectorSpriteModel(DM3DVectorSpriteModel *model)
{
    int i;
    for (i = 0; i < model->nbitmaps; i++)
    {
        if (model->bitmaps[i].img != NULL)
            SDL_FreeSurface(model->bitmaps[i].img);
    }

    dmFree(model->bitmaps);
    dmFree(model->vertices);
    dmFree(model->lines);
    dmFree(model->sprites);
    dmFree(model);
}