/************************************************************
  Ninja Sample
  COPYRIGHT (C) SEGA ENTERPRISES, LTD.

  タイトル ： ポリゴンモデルに当り判定をつける
  編集履歴 ： 98-05-13 製作開始
              98-06-19 SET5対応
************************************************************/

#include <shinobi.h>
#include <njdef.h>

void main(void)
{
	njSetBorderColor( 0x00000000 );
    njUserInit();
    while (njUserMain() == NJD_USER_CONTINUE) {
        njWaitVSync();
    }
    njUserExit();
}


/* グローバルセクション */

/* 外部定義のモデルデータ */
extern NJS_OBJECT   G_ObjectBox[];
extern NJS_OBJECT   G_ObjectSphere[];
extern NJS_OBJECT   G_ObjectCapsule[];


/* モデル用構造体 */
/* 球  */
typedef struct {

    float r;
    NJS_SPHERE coli;

} MODEL_SPHERE;
/* ボックス */
typedef struct {

    float   size;
    NJS_BOX coli;

} MODEL_BOX;
/* カプセル */
typedef struct {

    float r;
    Angle theta;
    Angle dt;
    NJS_POINT3  c1;
    NJS_POINT3  c2;
    NJS_CAPSULE coli;

} MODEL_CAPSULE;

/* モデル用共用体 */
typedef union {

    MODEL_SPHERE  sphere;
    MODEL_BOX     box;
    MODEL_CAPSULE capsule;

} MY_MODEL;

/* オブジェクト用構造体 */
enum TYPE {
       TYPE_SPHERE,
       TYPE_BOX,
       TYPE_CAPSULE,
       TYPE_END
};

typedef struct {

    enum TYPE type;

    NJS_POINT3 pos;
    NJS_POINT3 v;

    MY_MODEL   model;

} MY_OBJECT;

#define OBJECT_NUM  9
static MY_OBJECT G_Object[OBJECT_NUM];

/* マトリクス関連 */
#define MATRIX_DEPTH 12
#define VERTEX_MAX   4096

static NJS_MATRIX     *G_Matrix;  /* マトリクススタック */
static NJS_VERTEX_BUF *G_VBuf;  /* 演算用頂点バッファ */

/* 光源 ＆ 視点 */
static NJS_VIEW  G_View;
static NJS_LIGHT G_Light;

/* 関数のプロトタイプ */
void ObjectPhase(void);
void DrawObject(void);
void CollisionPhase(void);
int  CollisionObject(MY_OBJECT *obj1, MY_OBJECT *obj2);
void SetMaterial(float a, float r, float g, float b);
void CreateObject(int obj_num);
void CollisionEffect(MY_OBJECT *obj1, MY_OBJECT *obj2);
void CollisionEffectCapsule(MY_OBJECT *obj1, MY_OBJECT *obj2);
void PausePhase();
void ActivePhase();

/* コリジョン用定義 */
#define COLLISION_WEIGHT    40

/* ポーズ判定用フラグ */
Bool g_bPause = FALSE;

/**********************************************************************

	njSetBorderColor( 0x00000000 );
    njUserInit --- Ninjaシステム初期化関数

**********************************************************************/

void njUserInit(void)
{
    /* システムの初期化 */
    sbInitSystem(NJD_RESOLUTION_640x240_NTSCNI, NJD_FRAMEBUFFER_MODE_RGB565, 1);		/* 旧バージョンのコントロールボックスへの対応 */
	/* ＮＴＳＣ画面の縦横比を補正 */
    njSetAspect(1.0F, 0.45F);
    /* メモリの割り当て */
    G_Matrix = (NJS_MATRIX *)syMalloc(sizeof (NJS_MATRIX) * MATRIX_DEPTH);
    G_VBuf = (NJS_VERTEX_BUF *)syMalloc(sizeof (NJS_VERTEX_BUF) * VERTEX_MAX);
    /* 背景色の設定 */
    njSetBackColor(ARGB(0xff, 0xc0, 0, 0), ARGB(0xff, 0, 0xc0, 0), 
												ARGB(0xff, 0, 0, 0xc0));
    /* 頂点格納バッファの確保 */
    njInitVertexBuffer(VERTEX_MAX * 64 / 4, 0, VERTEX_MAX * 64 / 4, 0, 0);
	njInitPrint( NULL, 0, NJD_TEXFMT_ARGB_1555 );
    /* マトリクススタックの初期化 */
    njInitMatrix(G_Matrix, MATRIX_DEPTH, 1);
    /* 演算用頂点バッファの初期化 */
    njInit3D(G_VBuf, VERTEX_MAX);
    /* 光源の初期化 */
    njCreateLight(&G_Light, NJD_LAMBERT_DIR);
    /* ビューの初期化 */
    njInitView(&G_View);
    /* ビューの設定 */
    njSetView(&G_View);
    /* マテリアルを変更できるように設定 */
    njControl3D(NJD_CONTROL_3D_CONSTANT_MATERIAL);
    /* オブジェクトの生成 */
    CreateObject(OBJECT_NUM);
	/* テキストの色を指定 */
	njPrintColor(ARGB(0xff, 0xc0, 0xc0, 0xc0));
}


/**********************************************************************

    CreateObject --- オブジェクトを生成する

**********************************************************************/

void CreateObject(int obj_num)
{
    NJS_POINT3 pos;
    int counter;

    /* オブジェクトデータの初期化 */
    pos.x = -8.0F;
    pos.y =  8.0F;
    for (counter = 0; counter < obj_num; counter++) {
        G_Object[counter].type = counter % TYPE_END;
        switch(G_Object[counter].type) {
        case TYPE_SPHERE:
            /* 球データの初期化 */
            G_Object[counter].type = TYPE_SPHERE;
            G_Object[counter].pos.x =  pos.x;
            G_Object[counter].pos.y =  pos.y;
            G_Object[counter].pos.z = -20.0F;
            G_Object[counter].v.x = -0.2F;
            G_Object[counter].v.y =  0.2F;
            G_Object[counter].model.sphere.r = 1.5F;
            break;

        case TYPE_BOX:
            /* ボックスデータの初期化 */
            G_Object[counter].type = TYPE_BOX;
            G_Object[counter].pos.x = pos.x;
            G_Object[counter].pos.y = pos.y;
            G_Object[counter].pos.z = -20.0F;
            G_Object[counter].v.x = 0.1F;
            G_Object[counter].v.y = 0.1F;
            G_Object[counter].model.box.size = 2.0F;
            break;

        case TYPE_CAPSULE:
            /* カプセルデータの初期化 */
            G_Object[counter].type = TYPE_CAPSULE;
            G_Object[counter].pos.x = pos.x;
            G_Object[counter].pos.y = pos.y;
            G_Object[counter].pos.z =-20.0F;
            G_Object[counter].v.x = 0.05F;
            G_Object[counter].v.y = 0.05F;
            G_Object[counter].model.capsule.r = 1.0F;
            G_Object[counter].model.capsule.theta = NJM_DEG_ANG(0.0F);
            G_Object[counter].model.capsule.dt = NJM_DEG_ANG(2.0F);
            G_Object[counter].model.capsule.c1.x = 0.0F;
            G_Object[counter].model.capsule.c1.y = 2.0F;
            G_Object[counter].model.capsule.c1.z = 0.0F;
            G_Object[counter].model.capsule.c2.x = 0.0F;
            G_Object[counter].model.capsule.c2.y =-2.0F;
            G_Object[counter].model.capsule.c2.z = 0.0F;
            break;
        }
        pos.x += 8.0F;
        if (pos.x > 10.0F) {
            pos.x = -8.0F;
            pos.y -= 8.0F;
        }
    }
}


/**********************************************************************

    njUserMain --- Ninjaシステムメイン処理関数

**********************************************************************/

Sint32 njUserMain(void)
{
    NJS_PERIPHERAL *per;

    /* ジョイスティックポート１のペリフェラル情報を取得 */
    per = njGetPeripheral(NJD_PORT_A0);    

    /* ボタンが押されたかどうかチェック */
    if (per->press & NJD_DGT_ST) {
        g_bPause = !g_bPause;
    }

	/* ポーズ状態によって処理を切り替える */
	if (!g_bPause) {
	    njLightAllOn();
		ActivePhase();
	} else {
	    njLightAllOff();
	    PausePhase();
	}


    /* デバッグ情報の表示 */
    njPrintC(NJM_LOCATION(23, 4), "Ninja Sample COLLISION");

    return NJD_USER_CONTINUE;
}


/**********************************************************************

    ActivePhase --- アクティブ時処理関数

**********************************************************************/

void ActivePhase(void)
{
    /* マトリクススタックのクリア */
    njClearMatrix();

    /* オブジェクトの処理 */
    ObjectPhase();

    /* コリジョン処理 */
    CollisionPhase();

    /* オブジェクトの描画 */
    DrawObject();
}


/**********************************************************************

    PausePhase --- ポーズ時処理関数

**********************************************************************/

void PausePhase(void) 
{
    /* マトリクススタックのクリア */
    njClearMatrix();

    /* オブジェクトの描画 */
    DrawObject();

	/* ポーズのお知らせ */
    njPrintC(NJM_LOCATION(38, 30), "PAUSE");
}


/**********************************************************************

    ObjectPhase --- オブジェクトの処理

    argument: なし

**********************************************************************/

void ObjectPhase(void)
{
    MY_OBJECT *obj = G_Object;
    NJS_MATRIX mat;
    NJS_POINT3 c1, c2;
    int counter;

    for (counter = 0; counter < OBJECT_NUM; counter++) {

        /* オブジェクトの移動処理 */
        obj->pos.x += obj->v.x;
        if (obj->pos.x < -10.0F || obj->pos.x > 10.0F) {
            obj->v.x = -obj->v.x;
            obj->pos.x += obj->v.x * 2;
        }
        obj->pos.y += obj->v.y;
        if (obj->pos.y < -10.0F || obj->pos.y > 10.0F) {
            obj->v.y = -obj->v.y;
            obj->pos.y += obj->v.y * 2;
        }

        /* カプセルなら回転させる */
        if (obj->type == TYPE_CAPSULE) {
            obj->model.capsule.theta += obj->model.capsule.dt;
        }

        /* コリジョン用データの作成 */
        switch(obj->type) {

        case TYPE_SPHERE:
            obj->model.sphere.coli.c = obj->pos;
            obj->model.sphere.coli.r = obj->model.sphere.r;
            break;

        case TYPE_CAPSULE:
            njUnitMatrix(&mat);
            njTranslate(&mat, obj->pos.x, obj->pos.y, obj->pos.z);
            njRotateZ(&mat, obj->model.capsule.theta);
            njCalcPoint(&mat, &(obj->model.capsule.c1), &c1);
            njCalcPoint(&mat, &(obj->model.capsule.c2), &c2);
            obj->model.capsule.coli.c1 = c1;
            obj->model.capsule.coli.c2 = c2;
            obj->model.capsule.coli.r  = obj->model.capsule.r;
            break;
        
        case TYPE_BOX:
            obj->model.box.coli.v[0].x = obj->pos.x - obj->model.box.size;
            obj->model.box.coli.v[0].y = obj->pos.y + obj->model.box.size;
            obj->model.box.coli.v[0].z = obj->pos.z + obj->model.box.size;

            obj->model.box.coli.v[1].x = obj->pos.x - obj->model.box.size;
            obj->model.box.coli.v[1].y = obj->pos.y + obj->model.box.size;
            obj->model.box.coli.v[1].z = obj->pos.z - obj->model.box.size;

            obj->model.box.coli.v[2].x = obj->pos.x + obj->model.box.size;
            obj->model.box.coli.v[2].y = obj->pos.y + obj->model.box.size;
            obj->model.box.coli.v[2].z = obj->pos.z - obj->model.box.size;

            obj->model.box.coli.v[3].x = obj->pos.x + obj->model.box.size;
            obj->model.box.coli.v[3].y = obj->pos.y + obj->model.box.size;
            obj->model.box.coli.v[3].z = obj->pos.z + obj->model.box.size;

            obj->model.box.coli.v[4].x = obj->pos.x - obj->model.box.size;
            obj->model.box.coli.v[4].y = obj->pos.y - obj->model.box.size;
            obj->model.box.coli.v[4].z = obj->pos.z + obj->model.box.size;

            obj->model.box.coli.v[5].x = obj->pos.x - obj->model.box.size;
            obj->model.box.coli.v[5].y = obj->pos.y - obj->model.box.size;
            obj->model.box.coli.v[5].z = obj->pos.z - obj->model.box.size;

            obj->model.box.coli.v[6].x = obj->pos.x + obj->model.box.size;
            obj->model.box.coli.v[6].y = obj->pos.y - obj->model.box.size;
            obj->model.box.coli.v[6].z = obj->pos.z - obj->model.box.size;
            
            obj->model.box.coli.v[7].x = obj->pos.x + obj->model.box.size;
            obj->model.box.coli.v[7].y = obj->pos.y - obj->model.box.size;
            obj->model.box.coli.v[7].z = obj->pos.z + obj->model.box.size;
            break;
        }
        obj++;
    }
}


/**********************************************************************

    DrawObject --- オブジェクトの描画処理

    argument: なし

**********************************************************************/

void DrawObject(void)
{
    MY_OBJECT *obj = G_Object;
    int counter;

    for (counter = 0; counter < OBJECT_NUM; counter++) {
        switch(obj->type) {
        case TYPE_SPHERE:
            SetMaterial(1.0F, 1.0F, 0.0F, 0.0F);
            njPushMatrix(NULL);
                njTranslate(NULL, obj->pos.x, obj->pos.y, obj->pos.z);
                njScale(NULL, 0.1F, 0.1F, 0.1F);
                njDrawObject(G_ObjectSphere);
            njPopMatrix(1);
            break;

        case TYPE_BOX:
            SetMaterial(1.0F, 0.0F, 1.0F, 0.0F);
            njPushMatrix(NULL);
                njTranslate(NULL, obj->pos.x, obj->pos.y, obj->pos.z);
                njScale(NULL, 2.0F, 2.0F, 2.0F);
                njDrawObject(G_ObjectBox);
            njPopMatrix(1);
            break;

        case TYPE_CAPSULE:
            SetMaterial(1.0F, 0.0F, 0.0F, 1.0F);
            njPushMatrix(NULL);
                njTranslate(NULL, obj->pos.x, obj->pos.y, obj->pos.z);
                njRotateZ(NULL, obj->model.capsule.theta);
                njScale(NULL, 0.5F, 0.5F, 0.5F);
                njDrawObject(G_ObjectCapsule);
            njPopMatrix(1);
            break;
        }
        obj++;
    }
}


/**********************************************************************

    SetMaterial --- マテリアルを設定

    argument: float a（透明成分）
              float r（赤成分）
              float g（緑成分）
              float b（青成分）

**********************************************************************/

void SetMaterial(float a, float r, float g, float b)
{
    NJS_ARGB argb;

    /* マテリアルの設定 */
    argb.a = a;
    argb.r = r;
    argb.g = g;
    argb.b = b;
    njSetConstantMaterial(&argb);
}


/**********************************************************************

    CollisionPhase --- オブジェクトの当たり判定処理

    argument: なし

**********************************************************************/

void CollisionPhase(void)
{
    int cnt_src, cnt_dest;

    /* 全てのオブジェクトにたいして当たり判定を行う */
    for (cnt_src = 0; cnt_src < OBJECT_NUM; cnt_src++) {
        for (cnt_dest = cnt_src + 1; cnt_dest < OBJECT_NUM; cnt_dest++) {
            if (CollisionObject(&G_Object[cnt_src], &G_Object[cnt_dest])) {
                CollisionEffect(&G_Object[cnt_src], &G_Object[cnt_dest]);
            }
        }
    }
}


/**********************************************************************

    CollisionObject --- オブジェクトの当たり判定処理

    argument: MY_OBJECT *obj1（オブジェクト１）
              MY_OBJECT *obj2（オブジェクト２）

**********************************************************************/

int CollisionObject (MY_OBJECT *obj1, MY_OBJECT *obj2)
{
    switch(obj1->type) {
    case TYPE_SPHERE:
        if (obj2->type == TYPE_SPHERE) {
            return njCollisionCheckSS(&(obj1->model.sphere.coli),
                                   &(obj2->model.sphere.coli));
        } else if (obj2->type == TYPE_BOX) {
            return njCollisionCheckBS(&(obj2->model.box.coli),
                                   &(obj1->model.sphere.coli));
        } else if (obj2->type == TYPE_CAPSULE) {
            return njCollisionCheckSC(&(obj1->model.sphere.coli),
                                   &(obj2->model.capsule.coli));
        }
        break;

    case TYPE_BOX:
        if (obj2->type == TYPE_SPHERE) {
            return njCollisionCheckBS(&(obj1->model.box.coli),
                                   &(obj2->model.sphere.coli));
        } else if (obj2->type == TYPE_BOX) {
            return njCollisionCheckBB(&(obj1->model.box.coli),
                                   &(obj2->model.box.coli));
        } else if (obj2->type == TYPE_CAPSULE) {
            return njCollisionCheckBC(&(obj1->model.box.coli),
                                   &(obj2->model.capsule.coli));
        }
        break;

    case TYPE_CAPSULE:
        if (obj2->type == TYPE_SPHERE) {
            return njCollisionCheckSC(&(obj2->model.sphere.coli),
                                   &(obj1->model.capsule.coli));
        } else if (obj2->type == TYPE_BOX) {
            return njCollisionCheckBC(&(obj2->model.box.coli),
                                   &(obj1->model.capsule.coli));
        } else if (obj2->type == TYPE_CAPSULE) {
            return njCollisionCheckCC(&(obj1->model.capsule.coli),
                                   &(obj2->model.capsule.coli));
        }
        break;
    }
}


/**********************************************************************

    CollisionEffect --- 当たり判定による処理

    argument: MY_OBJECT *obj（オブジェクトへのポインタ）

**********************************************************************/

void CollisionEffect(MY_OBJECT *obj1, MY_OBJECT *obj2)
{
    obj1->v.x = (obj1->pos.x - obj2->pos.x) / COLLISION_WEIGHT;
    obj1->v.y = (obj1->pos.y - obj2->pos.y) / COLLISION_WEIGHT;
    obj1->v.z = (obj1->pos.z - obj2->pos.z) / COLLISION_WEIGHT;
    obj2->v.x = (obj2->pos.x - obj1->pos.x) / COLLISION_WEIGHT;
    obj2->v.y = (obj2->pos.y - obj1->pos.y) / COLLISION_WEIGHT;
    obj2->v.z = (obj2->pos.z - obj1->pos.z) / COLLISION_WEIGHT;

    obj1->pos.x += obj1->v.x;
    obj1->pos.y += obj1->v.y;
    obj2->pos.x += obj2->v.x;
    obj2->pos.y += obj2->v.y;
}


/**********************************************************************

    njUserExit --- Ninjaシステム終了処理関数

**********************************************************************/

void njUserExit(void)
{
	njExitPrint();
    /* メモリの開放 */
    syFree(G_Matrix);
    syFree(G_VBuf);
    /* システムの終了処理をします 最後に必ず呼び出してください */
    sbExitSystem();
}
