Mercurial > hg > Members > kono > Cerium
view TaskManager/Test/test_render/SceneGraph.cpp @ 205:efb1df3176f4
fix
author | gongo@localhost.localdomain |
---|---|
date | Tue, 27 Jan 2009 18:21:12 +0900 |
parents | 5c6c9667ac61 |
children | fe2cc32cd94d |
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->malloc(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(void) { init(); } 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; texture_id = -1; move = no_move; collision = no_collision; flag_remove = 0; flag_drawable = 1; sgid = -1; 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]; get_data(surface->children); } SceneGraph::~SceneGraph(void) { delete [] data; } /* XMLファイルからポリゴンを作成 */ void SceneGraph::createFromXMLfile(const char *xmlfile) { xmlDocPtr doc; xmlNodePtr cur; SceneGraphPtr root = NULL, tmp, 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; } /* ポリゴン(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); } } 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(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*)"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; } SceneGraphPtr SceneGraph::clone(void) { SceneGraphPtr p = new SceneGraph; memcpy(p, this, sizeof(SceneGraph)); // ポリゴンデータはとりあえずそのまま memcpy p->data = new float[p->size*3*3]; memcpy(p->data, this->data, sizeof(float)*p->size*3*3); 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; } delete node; 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; }