view Renderer/Engine/SceneGraph.cc @ 942:b2f7d2ad3804

FB mode is working again on Mac OS X.
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sat, 31 Jul 2010 18:37:10 +0900
parents 35efda39c2d9
children 33616b2789de
line wrap: on
line source

#include <iostream>
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_image.h>
#include <libxml/parser.h>
#include "SceneGraph.h"
#include "xml.h"
#include "sys.h"
#include "TextureHash.h"
#include "texture.h"
#include "TaskManager.h"

using namespace std;

SceneGraphPtr scene_graph = NULL;
SceneGraphPtr scene_graph_viewer = NULL;

static TextureHash texture_hash;
texture_list list[TABLE_SIZE];


extern int decode(char *cont, FILE *outfile);

static void
no_move(SceneGraphPtr self, void *sgroot_, int screen_w, int screen_h) {}

static void
no_collision(SceneGraphPtr self, void *sgroot_, int screen_w, int screen_h,
             SceneGraphPtr tree) {}

/**
 * 事前に計算したテクスチャの最大縮小率 scale まで、
 * テクスチャを 1/2 縮小していく。
 * このとき、テクスチャは TEXTURE_SPLIT_PIXELx2 のブロック (Tile) で分割し、
 * これらを連続したメモリ領域に格納していく。
 * 以下の (1), (2), (3) を Tapestry と呼ぶ
 *
 * 例 scale = 4 の場合
 *
 *   Tapestry(1) 1/1
 * +---+---+---+---+
 * | 0 | 1 | 2 | 3 |
 * +---+---+---+---+
 * | 4 | 5 | 6 | 7 |   (2) 1/2
 * +---+---+---+---+  +---+---+
 * | 8 | 9 | 10| 11|  | 16| 17|   (3) 1/4
 * +---+---+---+---+  +---+---+  +---+
 * | 12| 13| 14| 15|  | 18| 19|  | 20|
 * +---+---+---+---+  +---+---+  +---|
 *
 * (1)                                                 (2)             (3)
 *  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
 *  | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * | * | * | 14| 15| 16| 17| 18| 19| 20|
 *  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
 *
 * @param[in] tex_w         Width of orignal texture
 * @param[in] tex_h         Height of orignal texture
 * @param[in] tex_src       Original texture
 * @param[in] all_pixel_num Tapestry の合計 pixel 数
 * @param[in] scale         テクスチャの最大縮小率 (= 2^n)
 * @return (1) のアドレス
 */
static uint32*
makeTapestry(TaskManager *manager, int tex_w, int tex_h, uint32 *tex_src,
             int all_pixel_num, int scale_cnt)
{

    int t = 0;
    int diff = TEXTURE_SPLIT_PIXEL;
    int p_diff = 1;

    uint32 *tex_dest = (uint32*)manager->allocate(sizeof(int)*all_pixel_num);

    while (scale_cnt) {
        for (int y = 0; y < tex_h; y += diff) {
            for (int x = 0; x < tex_w; x += diff) {
                for (int j = 0; j < diff; j += p_diff) {
                    for (int i = 0; i < diff; i += p_diff) {
                        tex_dest[t++]
                            = tex_src[(x+i) + tex_w*(y+j)];
                    }
                }
            }
        }

        diff <<= 1;
        p_diff <<= 1;
        scale_cnt >>= 1;
    }

    return tex_dest;
}


/**
 * 何の情報も持ってない SceneGraph の生成
 * 今のところ、とりあえず木構造の繋がりに使うぐらい
 */
SceneGraph::SceneGraph()
{
    init();
    finalize = &SceneGraph::finalize_copy;

    this->name = "NULLPO";
}

/**
 * orig のコピーとして SceneGraph を生成する
 */
SceneGraph::SceneGraph(SceneGraphPtr orig)
{

    init();
    memcpy(this, orig, sizeof(SceneGraph));

    // コピーしない
    //flag_remove = 0;
    //flag_drawable = 1;
    next = NULL;
    prev = NULL;
    last = NULL;

    parent = NULL;
    brother = NULL;
    children = NULL;
    lastChild = NULL;

    finalize = &SceneGraph::finalize_copy;


    frame = 0;
}


/* construct polygon from xmlNode.  */
SceneGraph::SceneGraph(TaskManager *manager, xmlNodePtr surface)
{
    init();

    size = atoi((char *)xmlGetProp(surface,(xmlChar *)"size"));
    name = (char *)xmlGetProp(surface,(xmlChar *)"name");
    parent_name = (char *)xmlGetProp(surface,(xmlChar *)"parent");
    //texture_info = (texture_list_ptr)manager->allocate(sizeof(texture_list));
    //data = new float[size*3*3];

    for (int i = 0; i < 16; i++) {
      matrix[i] = 0;
      real_matrix[i] = 0;
    }

#if SPE_CREATE_POLYGON_CHECK

    for (int i = 0; i < 16; i++) {
      printf("%f\n",matrix[i]);
      printf("r %f\n",real_matrix[i]);
    }

#endif

    coord_xyz = (float*)manager->allocate(sizeof(float)*size*3);
    coord_tex = (float*)manager->allocate(sizeof(float)*size*3);
    normal    = (float*)manager->allocate(sizeof(float)*size*3);

    coord_pack_size = sizeof(float)*8*size*3;     // coord_pack_vertex size is 32byte. vertex num 3.
    coord_pack = (float*)manager->allocate(coord_pack_size);

    get_data(manager, surface->children);

    finalize = &SceneGraph::finalize_original;
}

void
SceneGraph::init()
{
    next = NULL;
    prev = NULL;
    last = NULL;

    parent = NULL;
    brother = NULL;
    children = NULL;
    lastChild = NULL;

    stack_xyz[0] = 0.0f;
    stack_xyz[2] = 0.0f;
    stack_xyz[1] = 0.0f;
    stack_angle[0] = 0.0f;
    stack_angle[1] = 0.0f;
    stack_angle[2] = 0.0f;

    size = 0;
    //data = NULL;
  
#if SPE_CREATE_POLYGON

    //tri_pack = NULL;
    //sg_matrix = NULL;
    //matrix = NULL;
    //real_matrix = NULL;
    //texture_info = NULL;

#else

    coord_xyz = NULL;
    normal = NULL;
    coord_tex = NULL;

#endif

    texture_id = -1;
    move = no_move;
    collision = no_collision;

    flag_remove = 0;
    flag_drawable = 1;
    sgid = -1;
    gid = -1;

    frame = 0;
}

SceneGraph::~SceneGraph()
{
    (this->*finalize)();
}

/**
 * xml ファイルから生成されたオリジナル SceneGraph なので
 * polygon data を削除
 */
void
SceneGraph::finalize_original()
{
    //delete [] data;

#if SPE_CREATE_POLYGON

    free(coord_pack);
    free(coord_xyz);
    free(coord_tex);
    free(normal);

#else 

    free(coord_xyz);
    free(coord_tex);
    free(normal);

#endif

}

/**
 * SceneGraph ID から生成された、コピー SceneGraph なので
 * polygon data は削除しない。オリジナルの方で削除する。
 */
void
SceneGraph::finalize_copy()
{
}


/**
 * add Children
 * 親の登録と、brother のリストへ加える
 *
 * @param child new child
 */
SceneGraphPtr
SceneGraph::addChild(SceneGraphPtr child)
{
    /* childrenのリストの最後に加える (brother として)*/
    if (this->lastChild != NULL) {
        SceneGraphPtr last = this->lastChild;
        last->brother = child;
	//child->parent = this;
	//return child;
    }
    
    this->lastChild = child;
    
    if (this->children == NULL) {
        this->children = child;
    }
    
    child->parent = this;

    return child;
}


/**
 * add Brother
 * addChild() でも brother の操作をしないといけないので、そっちに回す
 *
 * @param bro new Brother
 */
SceneGraphPtr
SceneGraph::addBrother(SceneGraphPtr bro)
{
    if (this->parent) {
        parent->addChild(bro);
    } else {
        fprintf(stderr, "error : SceneGraph::%s : %s doesn't have parent\n",
                __FUNCTION__, this->name);
    }

    return bro;
}

/* thisの子や子孫にnameのものが存在すればそいつを返す なければNULL.  */
SceneGraphPtr
SceneGraph::searchSceneGraph(const char *name)
{
    SceneGraphPtr tmp;
    SceneGraphPtr result;

    /* 本人か  */
    if( 0==strcmp(this->name, name) ) return this;

    /* 子供から再帰的に探す  */
    for(tmp = this->children; tmp; tmp = tmp->next) {
        if ((result=tmp->searchSceneGraph(name)) != NULL)
            return result;
    }

    /* 無かったら NULL.  */
    return NULL;
}

void
SceneGraph::tree_check()
{
    SceneGraphPtr t = this;

    while(t)
    {
        cout << "my_name : " << t->name << endl;
        if(t->children != NULL)
        {
            cout << "--move children : " << t->children->name << endl;
            t = t->children;
        }
        else if(t->brother != NULL)
        {
            cout << "--move brother : " << t->brother->name << endl;
            t = t->brother;
        }
        else
        {
            while(t)
            {
                if(t->brother != NULL)
                {
                    cout << "--move brother : " << t->brother->name << endl;
                    t = t->brother;
                    break;
                }
                else
                {
                    if(t->parent)
                    {
                        cout << "--move parent : " << t->parent->name << endl;
                    }
                    t = t->parent;
                }
            }
        }
    }
}


void
SceneGraph::print_member()
{
    cout << "size = " << size << endl;
    cout << "name = " << name << endl;
    cout << "parent_name = " << parent_name << endl;

    if (parent != NULL) {
        cout << "parent->name = " << parent->name << endl;
    }

    if (children != NULL) {
        cout << "children->name = " << children->name << endl;
    }
}


/*
 * surface nodeからポリゴンの情報を読み出す 再帰しない
 */
void
SceneGraph::get_data(TaskManager *manager, xmlNodePtr cur)
{
    //char *image_name;

    for(;cur;cur=cur->next)
    {
        if(!xmlStrcmp(cur->name,(xmlChar*)"coordinate"))
        {
            char *cont = (char *)xmlNodeGetContent(cur);
            pickup_coordinate(cont);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"normal"))
        {
            char *cont = (char *)xmlNodeGetContent(cur);
            pickup_normal(cont);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"model"))
        {
            char *cont = (char *)xmlNodeGetContent(cur);
            pickup_model(cont);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"texture"))
        {
            char *cont = (char *)xmlNodeGetContent(cur);
            pickup_texture(cont);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"imageflag"))
        {
            int id;
            char *filename = (char *)xmlGetProp(cur, (xmlChar *)"name");
            texture_hash.hash_regist(filename, id);
        }
        else if(!xmlStrcmp(cur->name,(xmlChar*)"image"))
        {
            get_image(manager, cur);
        }
    }
}

SDL_Surface*
SceneGraph::load_decode_image(char *image_name, xmlNodePtr cur)
{
    int fd = mkstemp(image_name);
    FILE *outfile = fdopen(fd, "wb");

    if (NULL == outfile) {
            cout << "error open file\n";
            return 0;
    }

    char *cont = (char *)xmlNodeGetContent(cur);
    //decode(cont, image_name);
    decode(cont, outfile);
    fclose(outfile);


/**
 * image を 32bit(RGBA) に変換する
 */
    SDL_Surface *texture_image = IMG_Load(image_name);
    if (!texture_image) return 0;
    SDL_Surface *tmpImage
        = SDL_CreateRGBSurface(SDL_HWSURFACE, texture_image->w,
                               texture_image->h, 32, redMask,
                               greenMask, blueMask, alphaMask);
    SDL_Surface *converted;
    converted = SDL_ConvertSurface(texture_image, tmpImage->format,
                                   SDL_HWSURFACE);
    if (converted != NULL) {
        SDL_FreeSurface(texture_image);
        texture_image = converted;
    }

    // this->gl_tex = SDL_GL_LoadTexture(texture_image);
    return texture_image;
}

int
SceneGraph::makeTapestries(TaskManager *manager, SDL_Surface *texture_image, int id) {
                uint32 *tapestry;
                int scale = 1;
                int tex_w = texture_image->w;
                int tex_h = texture_image->h;
                int all_pixel_num = 0;

                /**
                 * テクスチャの w or h が 8 pixel で分割できる間、
                 * 1/2 の縮小画像を作る。
                 * ここでは、最大の scale (1/scale) を見つける
                 *
                 * (ex)
                 *  (128,128) => 64,64 : 32,32: 16,16 : 8,8
                 *     scale = 16
                 *  (128, 64) => 64,32 : 32,16: 16,8
                 *     scale = 8
                 */
                while (tex_w % TEXTURE_SPLIT_PIXEL == 0 &&
                       tex_h % TEXTURE_SPLIT_PIXEL == 0) {
                    all_pixel_num += tex_w*tex_h;
                    tex_w >>= 1; /* tex_w /= 2 */
                    tex_h >>= 1;
                    scale <<= 1; /* scale *= 2 */
                }

                scale >>= 1;

                tapestry = makeTapestry(manager, texture_image->w, texture_image->h,
                                        (uint32*)texture_image->pixels,
                                        all_pixel_num, scale);

                list[id].t_w = texture_image->w;
                list[id].t_h = texture_image->h;
                list[id].pixels_orig = (Uint32*)texture_image->pixels;
                list[id].pixels = tapestry;
                list[id].scale_max = scale;
                list[id].texture_image = texture_image;

                return id;
    }

void
SceneGraph::get_image(TaskManager *manager, xmlNodePtr cur)
{
    char image_name[20] = "/tmp/image_XXXXXX";
    char *filename = (char *)xmlGetProp(cur, (xmlChar *)"name");

    if (filename == NULL || filename[0] == 0) {
        return;
    }

    /**
     * image_name を既に Load していれば何もしない
     */
    int tex_id;
    if (!texture_hash.hash_regist(filename, tex_id)) {

        SDL_Surface *texture_image = load_decode_image(image_name, cur);
	if (texture_image==0) {
	    printf("Can't load image %s\n",filename);
	    exit(0);
	}

        texture_id = makeTapestries(manager, texture_image, tex_id);

        if (unlink(image_name)) {
            cout << "unlink error\n";
        }
    } else {
        /**
         * 以前に Load されている Texture を共用
         */
        texture_id = tex_id;
    }

      // こんなことすると list[] のいみあるのかなーと
      // 微妙に思う、自分で書き換えた感想 by gongo
      texture_info.t_w = list[texture_id].t_w;
      texture_info.t_h = list[texture_id].t_h;;
      texture_info.pixels_orig = list[texture_id].pixels_orig;
      texture_info.pixels = list[texture_id].pixels;
      texture_info.scale_max = list[texture_id].scale_max;
      texture_info.texture_image = list[texture_id].texture_image;

}


void
SceneGraph::delete_data()
{
    SceneGraphPtr n = this->next, m;

    //n = this;
    //delete [] n->data;

    if (next) {
        while (n) {
            m = n->next;
            delete n;
            n = m;
        }
    }
}

/* move_func 実行 sgroot 渡す */
void
SceneGraph::move_execute(int w, int h)
{
    (*move)(this, this->sgroot, w, h);
}

void
SceneGraph::collision_check(int w, int h, SceneGraphPtr tree)
{
    (*collision)(this, this->sgroot, w, h, tree);
}

void
SceneGraph::create_sg_execute()
{
    (*create_sg)(this->sgroot, property, update_property);
}


void
SceneGraph::set_move_collision(move_func new_move,
                               collision_func new_collision)
{
    this->move = new_move;
    this->collision = new_collision;
}


void
SceneGraph::set_move_collision(move_func new_move,
                               collision_func new_collision, void *sgroot_)
{
    this->move = new_move;
    this->collision = new_collision;
    // add
    this->sgroot = sgroot_;
}

void
SceneGraph::set_move_collision(move_func new_move,
                               collision_func new_collision, create_sg_func new_create_sg)
{
    this->move = new_move;
    this->collision = new_collision;
    this->create_sg = new_create_sg;
}

void
SceneGraph::add_next(SceneGraphPtr next)
{
    /* next のリストの最後に加える */
    if (this->next != NULL) {
        SceneGraphPtr tmp = this->last;
        tmp->next = next;
    } else {
        this->next = next;
    }

    this->last = next;
}

/**
 * SceneGraph の clone
 * @return clone SceneGraph
 */
SceneGraphPtr
SceneGraph::clone() {
    SceneGraphPtr p = new SceneGraph(this);
    return p;
}

/**
 * SceneGraph の clone
 * 予め allocate されてる領域への placement new を行う
 *
 * @param buf clone 領域
 * @return clone SceneGraph
 */
SceneGraphPtr
SceneGraph::clone(void *buf) {
    SceneGraphPtr p = new(buf) SceneGraph(this);
    return p;
}

void
SceneGraph::remove()
{
    this->flag_remove = 1;
}

/**
 * tree から node を削除する
 *
 * @param tree SceneGraphTree
 * @return node削除後の SceneGraphTree
 */
SceneGraphPtr
SceneGraph::realRemoveFromTree(SceneGraphPtr tree)
{
    SceneGraphPtr node = this;
    SceneGraphPtr parent = node->parent;
    SceneGraphPtr ret = tree;

    if (parent) {
        SceneGraphPtr brother = parent->children;
        SceneGraphPtr p, p1 = NULL;

        p = brother;
        if (p) {
            if (p == node) {
                parent->children = NULL;
                parent->lastChild = NULL;
            } else {
                p1 = p->brother;

                while (p1 && p1 != node) {
                    p1 = p1->brother;
                    p = p->brother;
                }

                if (p1) {
                    p->brother = p1->brother;

                    // node が最後尾なら、lastChild を変更
                    if (parent->lastChild == p1) {
                        parent->lastChild = p;
                    }
                } else {
                    // Can't find remove node
                }
            }
        }
    } else {
        // 親が居ない = tree root なので
        // NULL を返す
        ret = NULL;
    }

    return ret;
}

/**
 * list から node を削除する
 *
 * @param list SceneGraphList
 * @return node削除後の SceneGraphList
 */
SceneGraphPtr
SceneGraph::realRemoveFromList(SceneGraphPtr list)
{
    SceneGraphPtr node = this;
    SceneGraphPtr prev = node->prev;
    SceneGraphPtr next = node->next;
    SceneGraphPtr ret = list;

    if (prev) {
        prev->next = next;
    } else {
        ret = next;
    }

    if (next) {
        next->prev = prev;
    }

    return ret;
}

int
SceneGraph::isRemoved()
{
    return flag_remove;
}

/**
 * 平行移動
 *
 * @param x Ttranslate in the x direction
 * @param y Ttranslate in the y direction
 * @param z Ttranslate in the z direction
 */
void
SceneGraph::translate(float x, float y, float z)
{
    this->xyz[0] = x;
    this->xyz[1] = y;
    this->xyz[2] = z;
}

/**
 * x 軸方向への平行移動
 *
 * @param x Ttranslate in the x direction
 */
void
SceneGraph::translateX(float x)
{
    this->xyz[0] = x;
}

/**
 * y 軸方向への平行移動
 *
 * @param y Ttranslate in the y direction
 */
void
SceneGraph::translateY(float y)
{
    this->xyz[1] = y;
}

/**
 * z 軸方向への平行移動
 *
 * @param z Ttranslate in the z direction
 */
void
SceneGraph::translateZ(float z)
{
    this->xyz[2] = z;
}

/* end */