/* blenddem.c
 *
 * A demo to show BRender's support for blending
 */

#include <stdio.h>
#include <dos.h>

#include "brender.h"
#include "dosio.h"

br_pixelmap *screen, *zbuffer, *backscreen, *palette, *blend;
br_actor *world, *camera, *light, *mainobj, *mainobj2, *obj2, *obj3,
         *branch1, *branch2;

/*
 * Show which keys do what
 */
void displayhelp(br_pixelmap *buffer, int x, int y, int colour)
{
    static char *help[] = {
        "BLEND DEMO - (c) Argonaut 1996",
        "",
        "h - help text on/off",
        "1 - specular hilighting on skull on/off",
        "2 - blending on torus on/off",
        "3 - blending on teapot on/off",
        "mouse - move skull",
        NULL
    };
    char **text = help;
    int i;

    for(i = y; *text; i += 7)
        BrPixelmapTextF(buffer, x - buffer->origin_x,
            i - buffer->origin_y, colour, BrFontProp4x6,
            *text++);
}

/*
 * Feature toggling functions
 */
br_boolean helpon = BR_TRUE;
br_boolean blend1on = BR_TRUE;
br_boolean blend2on = BR_TRUE;
br_boolean blend3on = BR_TRUE;

void helptoggle(void)
{
}

/*
 * Specular hilighting is simulated by environment mapping a glow-image,
 * with a blend table which produces a product of the texel and the screen
 * colour.
 */
void blend1toggle(void)
{
    if(blend1on)
        mainobj2->render_style = BR_RSTYLE_FACES;
    else
        mainobj2->render_style = BR_RSTYLE_NONE;
}

/*
 * Alpha-blended objects do not write to the z-buffer, so must be
 * rendered after all non-blended objects.
 * This is achieved by storing blended objects in a separate actor
 * hierarchy.
 */
void blend2toggle(void)
{
    if(blend2on){
        obj2->material->index_blend = blend;
        obj2->material->opacity = 128;
        BrMaterialUpdate(obj2->material, BR_MATU_ALL);
        BrActorRelink(branch2, obj2);
    } else {
        obj2->material->index_blend = NULL;
        obj2->material->opacity = 255;
        BrMaterialUpdate(obj2->material, BR_MATU_ALL);
        BrActorRelink(branch1, obj2);
    }
}

void blend3toggle(void)
{
    if(blend3on){
        obj3->material->index_blend = blend;
        obj3->material->opacity = 128;
        BrMaterialUpdate(obj3->material, BR_MATU_ALL);
        BrActorRelink(branch2, obj3);
    } else {
        obj3->material->index_blend = NULL;
        obj3->material->opacity = 255;
        BrMaterialUpdate(obj3->material, BR_MATU_ALL);
        BrActorRelink(branch1, obj3);
    }
}

void checkkeys(void)
{
    struct{
        int keytest;
        br_boolean *flag;
        void (*togglefn)(void);
    } keytable[] = {
        {SC_H, &helpon, helptoggle},
        {SC_1, &blend1on, blend1toggle},
        {SC_2, &blend2on, blend2toggle},
        {SC_3, &blend3on, blend3toggle},
        {0, NULL}
    };

    int i = 0;

    while(keytable[i].flag){
        if((DOSKeyTest(keytable[i].keytest,0,REPT_FIRST_DOWN))){
            *keytable[i].flag = !*keytable[i].flag;
            keytable[i].togglefn();
        }
        i++;
    }
}

void main(void)
{
	br_material *mats[20], *mat;
    br_matrix34 roll, temp;
    int n;
    long int mouse_x, mouse_y;
    unsigned long mouse_buttons;
    br_scalar t = 0, x = 0, y = 0, ox = 0, oy = 0, oz = 0;
    br_angle a1 = 0, a2 = 0, a3 = 0;
    int textcol, textshad;

    int i;

    BrBegin();

    screen = DOSGfxBegin(NULL);

    palette = BrPixelmapLoad("std.pal");
    if(palette)
        DOSGfxPaletteSet(palette);

    switch(screen->type){
        case BR_PMT_RGB_555:
        case BR_PMT_RGB_565:
        case BR_PMT_RGB_888:
            textcol = 0xffffff;
            textshad = 0x000000;
            break;
        case BR_PMT_INDEX_8:
        default :
            textcol = 63;
            textshad = 0;
            break;
    }

    screen->origin_x = screen->width/2;
    screen->origin_y = screen->height/2;
    backscreen = BrPixelmapMatch(screen, BR_PMMATCH_OFFSCREEN);
    zbuffer = BrPixelmapMatch(backscreen, BR_PMMATCH_DEPTH_16);

    backscreen->map = palette;
    
    blend = BrTableAdd(BrPixelmapLoad("blend.tab"));

    BrZbBegin(screen->type, BR_PMT_DEPTH_16);

	BrTableFindHook(BrTableFindFailedLoad);
	BrMapFindHook(BrMapFindFailedLoad);

    n = BrFmtScriptMaterialLoadMany("blenddem.mat", mats, BR_ASIZE(mats));
	BrMaterialAddMany(mats, n);

    world = BrActorAllocate(BR_ACTOR_NONE, NULL);
    branch1 = BrActorAllocate(BR_ACTOR_NONE, NULL);
    branch2 = BrActorAllocate(BR_ACTOR_NONE, NULL);
    camera = BrActorAllocate(BR_ACTOR_CAMERA, NULL);
    light = BrActorAllocate(BR_ACTOR_LIGHT, NULL);
    mainobj = BrActorAllocate(BR_ACTOR_MODEL, NULL);
    mainobj2 = BrActorAllocate(BR_ACTOR_MODEL, NULL);
    obj2 = BrActorAllocate(BR_ACTOR_MODEL, NULL);
    obj3 = BrActorAllocate(BR_ACTOR_MODEL, NULL);

    ((br_camera *) camera->type_data)->type = BR_CAMERA_PERSPECTIVE;
	((br_camera *) camera->type_data)->field_of_view = BR_ANGLE_DEG(45.0);
	((br_camera *) camera->type_data)->hither_z = BR_SCALAR(0.1);
    ((br_camera *) camera->type_data)->yon_z = BR_SCALAR(20.0);
    ((br_camera *) camera->type_data)->aspect = BR_SCALAR(320.0/240.0);
	camera->t.type = BR_TRANSFORM_MATRIX34;
    BrMatrix34Translate(&camera->t.t.mat, 0, 0, BR_SCALAR(5));

    ((br_light *) light->type_data)->type = BR_LIGHT_DIRECT;
    light->t.type = BR_TRANSFORM_MATRIX34;
    BrMatrix34RotateX(&light->t.t.mat, BR_ANGLE_DEG(-30));
    BrLightEnable(light);

    mainobj->model = BrModelLoad("skull.dat");
    mainobj->material = BrMaterialFind("tex1");
    mainobj2->model = mainobj->model;
    mainobj2->material = BrMaterialFind("shine");
    BrModelAdd(mainobj->model);

    obj2->model = BrModelLoad("torus.dat");
    obj2->material = BrMaterialFind("tex2");
    BrModelAdd(obj2->model);
    obj3->model = BrModelLoad("teapot.dat");
    obj3->material = BrMaterialFind("tex3");
    BrModelAdd(obj3->model);

    BrActorAdd(world, camera);
    BrActorAdd(world, light);
    BrActorAdd(world, branch1);
    BrActorAdd(world, branch2);
    BrActorAdd(branch1, mainobj);
    BrActorAdd(mainobj, mainobj2);
    BrActorAdd(branch2, obj2);
    BrActorAdd(branch2, obj3);

    BrEnvironmentSet(world);

    DOSKeyBegin();
    DOSMouseBegin();

    DOSMouseRead(&mouse_x, &mouse_y, &mouse_buttons);

    BrMatrix34Identity(&roll);

    while(!(DOSKeyTest(SC_Q,0,REPT_FIRST_DOWN))){
        a1 += BR_ANGLE_DEG(1);
        a2 += BR_ANGLE_DEG(2);
        a3 += BR_ANGLE_DEG(3);

        checkkeys();

        BrPixelmapFill(backscreen, 0);
        BrPixelmapFill(zbuffer, 0xffffffff);

        BrZbSceneRenderBegin(world, camera, backscreen, zbuffer);
            BrZbSceneRenderAdd(branch1);
            BrZbSceneRenderAdd(branch2);
        BrZbSceneRenderEnd();
        if(helpon){
            displayhelp(backscreen, 12, 12, textshad);
            displayhelp(backscreen, 11, 11, textcol);
        }
        BrPixelmapDoubleBuffer(screen, backscreen);

        mouse_x = mouse_y = 0;
		DOSMouseRead(&mouse_x, &mouse_y, &mouse_buttons);

        if((mouse_buttons & BR_MSM_BUTTONL)){
            ox = BR_ADD(ox, BR_MUL(BR_SCALAR(mouse_x), BR_SCALAR(0.01)));
            oy = BR_SUB(oy, BR_MUL(BR_SCALAR(mouse_y), BR_SCALAR(0.01)));
        } else if((mouse_buttons & BR_MSM_BUTTONR)){
            ox = BR_ADD(ox, BR_MUL(BR_SCALAR(mouse_x), BR_SCALAR(0.01)));
            oz = BR_ADD(oz, BR_MUL(BR_SCALAR(mouse_y), BR_SCALAR(0.01)));
        } else {
            BrMatrix34RollingBall(&temp, mouse_x, -mouse_y, 800);
            BrMatrix34Post(&roll, &temp);
        }
        BrMatrix34Copy(&mainobj->t.t.mat, &roll);
        BrMatrix34PostTranslate(&mainobj->t.t.mat, ox, oy, oz);

        /* Some random movement here */
        BrMatrix34Translate(&obj2->t.t.mat,
            BR_MUL(BR_SIN(a1),BR_SCALAR(0.7)),
            BR_MUL(BR_SIN(a1+BR_ANGLE_DEG(30)),BR_SCALAR(1.1)),
            BR_MUL(BR_SIN(a1+BR_ANGLE_DEG(140)),BR_SCALAR(1.9)));
        BrMatrix34PreRotateX(&obj2->t.t.mat, a1);
        BrMatrix34PreRotateY(&obj2->t.t.mat, a3);
        BrMatrix34PostRotateZ(&obj2->t.t.mat, a2);

        BrMatrix34Translate(&obj3->t.t.mat,
            BR_MUL(BR_SIN(a1+BR_ANGLE_DEG(90)),BR_SCALAR(-1.3)),
            BR_MUL(BR_SIN(a1),BR_SCALAR(0.5)),
            BR_MUL(BR_SIN(a1),BR_SCALAR(0.6)));
        BrMatrix34PreRotateX(&obj3->t.t.mat, a3);
        BrMatrix34PreRotateZ(&obj3->t.t.mat, a1);
    }

    DOSGfxEnd();

    BrEnd();
}



