view Renderer/test_render/SceneGraph.cpp @ 283:55ea4465b1a2

fix test_render
author e065746@localhost.localdomain
date Fri, 05 Jun 2009 16:49:12 +0900
parents
children
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;
struct texture_list list[TABLE_SIZE];

// TextureHash.cpp
extern int id_count;

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

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

static void
no_collision(SceneGraphPtr self, 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(int tex_w, int tex_h, uint32 *tex_src,
	     int all_pixel_num, int scale_cnt)
{
    uint32 *tex_dest;

    int t = 0;
    int diff = TEXTURE_SPLIT_PIXEL;
    int p_diff = 1;
	
    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(void)
{
    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(xmlNodePtr surface)
{
    init();

    size = atoi((char *)xmlGetProp(surface,(xmlChar *)"size"));
    name = (char *)xmlGetProp(surface,(xmlChar *)"name");
    parent_name = (char *)xmlGetProp(surface,(xmlChar *)"parent");

    //data = new float[size*3*3];
    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);

    get_data(surface->children);

    finalize = &SceneGraph::finalize_original;
}

void
SceneGraph::init(void)
{
    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;
    coord_xyz = NULL;
    normal = NULL;
    coord_tex = NULL;

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

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

    frame = 0;
}

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

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

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

/* XMLファイルからポリゴンを作成  */
void
SceneGraph::createFromXMLfile(const char *xmlfile)
{
    xmlDocPtr doc;
    xmlNodePtr cur;
    SceneGraphPtr root = NULL, tmp = NULL, parent;
    
    /* パース DOM生成 */
    doc = xmlParseFile(xmlfile);
    cur = xmlDocGetRootElement(doc);

    /* ??  */
    xmlStrcmp(cur->name,(xmlChar*)"OBJECT-3D");

    /* XMLのノードを一つずつ解析  */
    for (cur=cur->children; cur; cur=cur->next) {
	/* 扱うのはsurfaceオンリー  */
	//if (xmlStrcmp(cur->name,(xmlChar*)"surface") != 0) {
	//    continue;
	//}
      if (!xmlStrcmp(cur->name,(xmlChar*)"surface")) {
	/* ポリゴン(SceneGraph)生成  */
	tmp = new SceneGraph(cur);
	if ( tmp->parent_name==NULL || 0==strcmp(tmp->parent_name, "NULL")) {
	    /* このsurfaceがroot  */
	    root = tmp;
	    scene_graph = tmp;
	} else {
	    /* 親はこのsurfaceより前に定義されているものとする (していい?)  */
	    //  ここで parent_name を用いるのは間違っていて、
	    //   *cur->properties->children から探すべきらしい kono
	    parent = root->searchSceneGraph(tmp->parent_name);
	    if (parent==NULL) {
		fprintf(stderr, "[%s] No such parent %s\n",
			tmp->name, tmp->parent_name);
		root->addChild(tmp);
	    } else {
		parent->addChild(tmp);
	    }

	    scene_graph->add_next(tmp);
	}
      }else if (!xmlStrcmp(cur->name,(xmlChar*)"image")) {
	
	char *cont;
	char image_name[20] = "/tmp/image_XXXXXX";
	char *filename = (char *)xmlGetProp(cur, (xmlChar *)"name");
	int fd = mkstemp(image_name);
	FILE *outfile = fdopen(fd, "wb");
	if(NULL == outfile)
	  {
	    cout << "error open file\n";
	  }
	cont = (char *)xmlNodeGetContent(cur);
	decode(cont, outfile);
	fclose(outfile);
		
	int tex_id = texture_hash.hash_regist(filename);
	if (tex_id < 0) 
	  {
	    
	    texture_image = IMG_Load(image_name);
	    
	    /**
	     * image を 32bit(RGBA) に変換する
	     */
	    
	    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;
	    }
	    
	    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(texture_image->w, texture_image->h,
				    (uint32*)texture_image->pixels,
				    all_pixel_num,
				    scale);
	    
	    list[id_count-1].t_w = texture_image->w;
	    list[id_count-1].t_h = texture_image->h;
	    list[id_count-1].pixels_orig = (Uint32*)texture_image->pixels;
	    list[id_count-1].pixels = tapestry;
	    list[id_count-1].scale_max = scale;
	    
	    tmp->texture_id = id_count-1;
	    tmp->texture_info.t_w = texture_image->w;
	    tmp->texture_info.t_h = texture_image->h;
	    tmp->texture_info.pixels_orig = (Uint32*)texture_image->pixels;
	    tmp->texture_info.pixels = tapestry;
	    tmp->texture_info.scale_max = scale;

	    if (unlink(image_name))
	      {
		cout << "unlink error\n";
	      }
	    
	  } else {
	  /**
	   * 以前に Load されている Texture を共用
	   */
	  
	  tmp->texture_id = tex_id;
	  
	  // こんなことすると list[] のいみあるのかなーと
	  // 微妙に思う、自分で書き換えた感想 by gongo
	  
	  tmp->texture_info.t_w = list[tex_id].t_w;
	  tmp->texture_info.t_h = list[tex_id].t_h;;
	  tmp->texture_info.pixels_orig = list[tex_id].pixels_orig;
	  tmp->texture_info.pixels = list[tex_id].pixels;
	  tmp->texture_info.scale_max = list[tex_id].scale_max;
	  
	}
      }else {
	continue;
      }
    }
  
    xmlFreeDoc(doc);

    //return root;
    scene_graph_viewer = root;
}


/**
 * 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;
    }

    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(void)
{
    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(void)
{
    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(xmlNodePtr cur)
{
    char *cont;
    //char *image_name;

    for(;cur;cur=cur->next)
    {
	if(!xmlStrcmp(cur->name,(xmlChar*)"coordinate"))
        {
	    cont = (char *)xmlNodeGetContent(cur);
	    pickup_coordinate(cont);
        }
	else if(!xmlStrcmp(cur->name,(xmlChar*)"normal"))
        {
	    cont = (char *)xmlNodeGetContent(cur);
	    pickup_normal(cont);
        }
	else if(!xmlStrcmp(cur->name,(xmlChar*)"model"))
        {
	    cont = (char *)xmlNodeGetContent(cur);
	    pickup_model(cont);
        }
	else if(!xmlStrcmp(cur->name,(xmlChar*)"texture"))
        {
	    cont = (char *)xmlNodeGetContent(cur);
	    pickup_texture(cont);
	}
	else if(!xmlStrcmp(cur->name,(xmlChar*)"imageflag"))
        {
	  char *filename = (char *)xmlGetProp(cur, (xmlChar *)"name");
	  texture_hash.hash_regist(filename);
	}
	else if(!xmlStrcmp(cur->name,(xmlChar*)"image"))
        {
	    char image_name[20] = "/tmp/image_XXXXXX";
	    char *filename = (char *)xmlGetProp(cur, (xmlChar *)"name");
	    int fd = mkstemp(image_name);
	    FILE *outfile = fdopen(fd, "wb");
	    if(NULL == outfile)
	    {
		cout << "error open file\n";
	    }
	    cont = (char *)xmlNodeGetContent(cur);
	    //decode(cont, image_name);
	    decode(cont, outfile);
	    fclose(outfile);

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

		texture_image = IMG_Load(image_name);
		
		/**
		 * image を 32bit(RGBA) に変換する
		 */
		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;
		}

		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(texture_image->w, texture_image->h,
					(uint32*)texture_image->pixels,
					all_pixel_num, scale);

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

		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;
	}
    }
}


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

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

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

void
SceneGraph::move_execute(int w, int h)
{
    (*move)(this, w, h);
}

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

void
SceneGraph::all_execute(int screen_w, int screen_h)
{
    SceneGraphPtr top = this;
    SceneGraphPtr t = top;

    while (t) {
	t->move_execute(screen_w, screen_h);
	t->collision_check(screen_w, screen_h, top);

	t->frame++;

	if (t->parent != NULL) {
	    get_matrix(t->matrix, t->angle, t->xyz, t->parent->matrix);
	} else {
	    get_matrix(t->matrix, t->angle, t->xyz, NULL);
	}

	if (t->children != NULL) {
	    t = t->children;
	} else if (t->brother != NULL) {
	    t = t->brother;
	} else {
	    while (t) {
		if (t->brother != NULL) {
		    t = t->brother;
		    break;
		} else {
		    if (t->parent == NULL) {
			t = NULL;
			break;
		    } else {
			t = t->parent;
		    }
		}
	    }
	}
    }
}

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

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

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(void) {
    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(void)
{
    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(void)
{
    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;
}