//////////////////////////////////////////////////////////////////////
//
// Project  : BRender Z sorted Sample
//            Copyright (c) 1996 Argonaut Technologies Ltd.
//            All rights reserved.
//
// Author   : Neela Dass
//
// File     : MINIMAL.C
//
// Requires : BRender V1.3
//            (DOS)
//
// Comments : Z sort sample to accompany Z sort white paper
//
//////////////////////////////////////////////////////////////////////

#include <brender.h>
#include <dosio.h>

void main()
{
    br_pixelmap  *screen_buffer, *back_buffer, *palette, *shade;
    br_actor *world, *ship, *pivot, *observer, *torus, *earth, *teapot,
    *back, *light1, *light2;
    br_material *mats[20], *mat;
    br_angle theta=0;
    br_matrix34 rotation;

    int i,n;

    long mouse_x, mouse_y;
    unsigned long mouse_buttons;

// Pointer to primitive heap
    void *primitives;

    br_order_table *primary_order_table;
    br_order_table *back_table;

//Initialize Renderer and Set Screen Resolution

	BrBegin();
    screen_buffer = DOSGfxBegin("MCGA");

//Allocate memory for primitive heap

	primitives = BrMemAllocate(2000000,BR_MEMORY_APPLICATION);

//Initialise the z-sort renderer.

	BrZsBegin(screen_buffer->type,primitives,2000000);

//Load up materials

	BrTableFindHook(BrTableFindFailedLoad);
	BrMapFindHook(BrMapFindFailedLoad);

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

// Load a shade table

	shade = BrPixelmapLoad("shade.tab");

	if (shade == NULL)
        BR_ERROR0("Could't load shade.tab");

// Put shade table into registry

	BrTableAdd(shade);


// Setup CLUT (ignored in true-colour)


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

// Allocate other buffers

    back_buffer = BrPixelmapMatch(screen_buffer,BR_PMMATCH_OFFSCREEN);
    back_buffer->origin_x=back_buffer->width/2;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                back_buffer->origin_y=back_buffer->height/2;

// Build the World Database - set up the hierarchy
    

    world = BrActorAllocate(BR_ACTOR_NONE,NULL);

    light1 = BrActorAdd(world, BrActorAllocate(BR_ACTOR_LIGHT, NULL));
	((br_light *) light1->type_data)->type = BR_LIGHT_DIRECT;
	((br_light *) light1->type_data)->attenuation_c = BR_SCALAR(1.0);
	light1->t.type = BR_TRANSFORM_MATRIX34;
	BrMatrix34RotateY(&light1->t.t.mat, BR_ANGLE_DEG(35));
	BrMatrix34RotateX(&light1->t.t.mat, BR_ANGLE_DEG(35));

    light2 = BrActorAdd(world, BrActorAllocate(BR_ACTOR_LIGHT, NULL));
	((br_light *) light2->type_data)->type = BR_LIGHT_DIRECT;
	((br_light *) light2->type_data)->attenuation_c = BR_SCALAR(1.0);
	light2->t.type = BR_TRANSFORM_MATRIX34;
	BrMatrix34RotateY(&light2->t.t.mat, BR_ANGLE_DEG(-35));
	BrMatrix34RotateX(&light2->t.t.mat, BR_ANGLE_DEG(-90));

// Mustn't forget to turn the light on

	BrLightEnable(light1);
	BrLightEnable(light2);
                  

// Load and Position Camera Actor

// Now place the main camera from which we will view the system
    observer = BrActorAdd(world, BrActorAllocate(BR_ACTOR_CAMERA, NULL));
    ((br_camera *) observer->type_data)->type = BR_CAMERA_PERSPECTIVE_OLD;
    ((br_camera *) observer->type_data)->field_of_view = BR_ANGLE_DEG(45.0);
    ((br_camera *) observer->type_data)->hither_z = BR_SCALAR(0.1);
    ((br_camera *) observer->type_data)->yon_z = BR_SCALAR(20.0);
    ((br_camera *) observer->type_data)->aspect = BR_SCALAR(1.46);
    observer->t.type = BR_TRANSFORM_MATRIX34;
    BrMatrix34Translate(&observer->t.t.mat,
	    	BR_SCALAR(0.0), BR_SCALAR(0.0), BR_SCALAR(2.0));

// Set up actor to be always at the back of the scene

    back = BrActorAdd(world, BrActorAllocate(BR_ACTOR_MODEL, NULL));
    back->model = BrModelLoad("br_box.dat");
    if (back->model == NULL)
		BR_ERROR0("Couldn't load br_box.dat");
    BrModelAdd(back->model);
    back->material = BrMaterialFind("brbox");
    back->t.type = BR_TRANSFORM_MATRIX34;


// Set up actor to be used to store the data that would allow manipulation of
// the earth, ship,teapot and torus with the mouse.

	pivot = BrActorAdd(world,BrActorAllocate(BR_ACTOR_NONE,NULL));
	pivot->t.type = BR_TRANSFORM_MATRIX34;

	earth = BrActorAdd(pivot, BrActorAllocate(BR_ACTOR_MODEL, NULL));
	earth->model = BrModelLoad("sph32.dat");
	if (earth->model == NULL)
	    BR_ERROR0("Couldn't load sph32.dat");
    BrModelAdd(earth->model);
    earth->material = BrMaterialFind("earth");
    earth->t.type = BR_TRANSFORM_MATRIX34;

    ship = BrActorAdd(earth, BrActorAllocate(BR_ACTOR_MODEL, NULL));
    ship->model = BrModelLoad("ship.dat");
    if (ship->model == NULL)
        BR_ERROR0("Couldn't load ship.dat");
    BrModelAdd(ship->model);
    ship->material = BrMaterialFind("SPACE_SHIP");
    ship->t.type = BR_TRANSFORM_MATRIX34;


// Set up actors that would have their order tables

    teapot = BrActorAdd(pivot, BrActorAllocate(BR_ACTOR_MODEL, NULL));
    teapot->model = BrModelLoad("teapot.dat");
    if (teapot->model == NULL)
        BR_ERROR0("Couldn't load teapot.dat");
    BrModelAdd(teapot->model);
    teapot->material = BrMaterialFind("moon");
    teapot->t.type = BR_TRANSFORM_MATRIX34;

    torus = BrActorAdd(pivot, BrActorAllocate(BR_ACTOR_MODEL, NULL));
    torus->model = BrModelLoad("torus.dat");
    if (torus->model == NULL)
        BR_ERROR0("Couldn't load torus.dat");
    BrModelAdd(torus->model);
    torus->material = BrMaterialFind("moon");
    torus->t.type = BR_TRANSFORM_MATRIX34;

// Allocate an order table to be the primary order table, with 3000 buckets. The z-range
// is pre-set and the primitives are inserted into the order table using the z value of
// the nearest vertex.

    primary_order_table = BrZsOrderTableAllocate(3000, BR_ORDER_TABLE_LEAVE_BOUNDS, BR_SORT_MIN);

// 	The pre-set depth range of 0-300 units over which to sort.

    primary_order_table->min_z = BR_SCALAR(0);
    primary_order_table->max_z = BR_SCALAR(300);


// Set up an order table for the model actor,back, by choosing sort_z to be large, this would ensure
// that it would always appear at the back of the scene. The z range is recalculated from the bounds
// of each actor that uses it, just before each use and primitives inserted using its furthest vertices.


    back_table = BrZsOrderTableAllocate(1024, BR_ORDER_TABLE_NEW_BOUNDS, BR_SORT_MAX);
    back_table->sort_z = BR_SCALAR(300);
    BrZsActorOrderTableSet(back, back_table);

// Secondary order tables are set up for the torus and the teapot. Note for increased accuracy in zsorting within an order table,
// one can increase the number of buckets and manipulate the sort_z values to control where an order table is entered into the
// list of order tables.

    BrZsActorOrderTableSet(torus, BrZsOrderTableAllocate(1024, BR_ORDER_TABLE_NEW_BOUNDS | BR_ORDER_TABLE_SORT_FAR, BR_SORT_MAX));

    BrZsActorOrderTableSet(teapot, BrZsOrderTableAllocate(1024, BR_ORDER_TABLE_NEW_BOUNDS | BR_ORDER_TABLE_SORT_NEAR, BR_SORT_MAX));

// Enables the use of the primary order table between whose buckets all order tables are then rendered.

    BrZsOrderTablePrimaryEnable(primary_order_table);

// Set up primary order table at the root
    BrZsActorOrderTableSet(world, primary_order_table);
    DOSMouseBegin();
    DOSKeyBegin();

// Animation Loop

    while (1) {

	    BrPixelmapFill(back_buffer,0);

// Place rotated earth and ship, back, torus and teapot

        ++theta;
                 
        BrMatrix34Scale(&earth->t.t.mat,
        	BR_SCALAR(0.2), BR_SCALAR(0.2), BR_SCALAR(0.2));
        BrMatrix34PostRotateX(&earth->t.t.mat, BR_ANGLE_DEG(90));
        BrMatrix34PreRotateZ(&earth->t.t.mat, BR_ANGLE_DEG(-3 * theta));
        BrMatrix34PostTranslate(&earth->t.t.mat,
            BR_SCALAR(0.0), BR_SCALAR(0.0), BR_SCALAR(0.0));

        BrMatrix34Scale(&ship->t.t.mat,
            BR_SCALAR(0.1), BR_SCALAR(0.1), BR_SCALAR(0.1));
        BrMatrix34PostRotateX(&ship->t.t.mat, BR_ANGLE_DEG(-90.0));
        BrMatrix34PostTranslate(&ship->t.t.mat,
                 BR_SCALAR(2.0), BR_SCALAR(1.0), BR_SCALAR(0.0));

        BrMatrix34Scale(&back->t.t.mat,
                   BR_SCALAR(2.5), BR_SCALAR(2.5), BR_SCALAR(2.5));
        BrMatrix34PostRotateX(&back->t.t.mat, BR_ANGLE_DEG(180));
        BrMatrix34PreRotateZ(&back->t.t.mat, BR_ANGLE_DEG(0));
        BrMatrix34PostTranslate(&back->t.t.mat,
                  BR_SCALAR(0.0), BR_SCALAR(0.0), BR_SCALAR(-4.0));


        BrMatrix34Scale(&torus->t.t.mat,
        	BR_SCALAR(0.3), BR_SCALAR(0.3), BR_SCALAR(0.3));
        BrMatrix34PreRotateZ(&torus->t.t.mat, BR_ANGLE_DEG(-3 * theta));

        BrMatrix34Scale(&teapot->t.t.mat,
			BR_SCALAR(0.2), BR_SCALAR(0.2), BR_SCALAR(0.2));
        BrMatrix34PreRotateX(&teapot->t.t.mat, BR_ANGLE_DEG(-90));
        BrMatrix34PostTranslate(&teapot->t.t.mat,
            BR_SCALAR(-0.2), BR_SCALAR(0.6), BR_SCALAR(-0.2));
        BrMatrix34PostRotateY(&teapot->t.t.mat, BR_ANGLE_DEG(-2 * theta));

        BrZsSceneRender(world,observer,back_buffer);
        BrPixelmapDoubleBuffer(screen_buffer,back_buffer);

// Allow user to move the pivot actor around

        mouse_x = mouse_y = 0;
                   
        DOSMouseRead(&mouse_x,&mouse_y,&mouse_buttons);
        if (mouse_x || mouse_y) {
            if (mouse_buttons & BR_MSM_BUTTONL) {
                BrMatrix34PostTranslate(&pivot->t.t.mat,
        			BR_MUL(BR_SCALAR(mouse_x),BR_SCALAR(0.01)),
                    BR_MUL(BR_SCALAR(-mouse_y),BR_SCALAR(0.01)),0);
            } 
			else if (mouse_buttons & BR_MSM_BUTTONR) {
                BrMatrix34PostTranslate(&pivot->t.t.mat,
                    BR_MUL(BR_SCALAR(mouse_x),BR_SCALAR(0.01)),0,
                    BR_MUL(BR_SCALAR(-mouse_y),BR_SCALAR(0.01)));
            } 
			else {
                BrMatrix34RollingBall(&rotation,mouse_x,-mouse_y,800);
                BrMatrix34Pre(&pivot->t.t.mat, &rotation); 
            }

        }

        if(DOSKeyTest(SC_Q,0,REPT_FIRST_DOWN))
			break;
	}

    BrPixelmapFree(back_buffer);
	BrZsEnd();
	DOSGfxEnd();
	DOSKeyEnd();
	DOSMouseEnd();
	BrEnd();
}
