John@15: #include nuclear@25: #include nuclear@26: #include nuclear@22: #include "scene.h" nuclear@6: nuclear@26: nuclear@26: static int build_kdtree(KDNode *kd); nuclear@26: static float eval_cost(const std::list &faces, const AABBox &aabb, int axis); nuclear@26: static void free_kdtree(KDNode *node); nuclear@26: nuclear@26: nuclear@26: static int accel_param[NUM_ACCEL_PARAMS] = { nuclear@26: 75, // max tree depth nuclear@26: 0, // max items per node (0 means ignore limit) nuclear@26: 5, // estimated traversal cost nuclear@26: 15 // estimated interseciton cost nuclear@26: }; nuclear@26: nuclear@26: nuclear@26: void set_accel_param(int p, int v) nuclear@26: { nuclear@26: assert(p >= 0 && p < NUM_ACCEL_PARAMS); nuclear@26: accel_param[p] = v; nuclear@26: } nuclear@26: nuclear@26: John@15: #define FEQ(a, b) (fabs((a) - (b)) < 1e-8) John@15: bool Face::operator ==(const Face &f) const John@15: { John@15: for(int i=0; i<3; i++) { John@15: for(int j=0; j<3; j++) { John@15: if(!FEQ(v[i].pos[j], f.v[i].pos[j])) { John@15: return false; John@15: } John@15: if(!FEQ(v[i].normal[j], f.v[i].normal[j])) { John@15: return false; John@15: } John@15: } John@15: if(!FEQ(normal[i], f.normal[i])) { John@15: return false; John@15: } John@15: } John@15: return true; John@15: } John@15: nuclear@25: float AABBox::calc_surface_area() const nuclear@25: { nuclear@25: float area1 = (max[0] - min[0]) * (max[1] - min[1]); nuclear@25: float area2 = (max[3] - min[3]) * (max[1] - min[1]); nuclear@25: float area3 = (max[0] - min[0]) * (max[3] - min[3]); nuclear@25: nuclear@25: return 2.0f * (area1 + area2 + area3); nuclear@25: } nuclear@25: nuclear@26: KDNode::KDNode() nuclear@26: { nuclear@26: axis = 0; nuclear@26: pt = 0.0; nuclear@26: left = right = 0; nuclear@26: num_faces = 0; nuclear@26: } nuclear@26: nuclear@25: nuclear@24: Scene::Scene() nuclear@24: { nuclear@24: facebuf = 0; nuclear@24: num_faces = -1; nuclear@24: kdtree = 0; nuclear@24: } nuclear@24: nuclear@24: Scene::~Scene() nuclear@24: { nuclear@24: delete [] facebuf; nuclear@24: } nuclear@24: nuclear@13: bool Scene::add_mesh(Mesh *m) nuclear@13: { nuclear@13: // make sure triangles have material ids nuclear@13: for(size_t i=0; ifaces.size(); i++) { nuclear@13: m->faces[i].matid = m->matid; nuclear@13: } nuclear@24: nuclear@24: try { nuclear@24: meshes.push_back(m); nuclear@24: } nuclear@24: catch(...) { nuclear@24: return false; nuclear@24: } nuclear@24: nuclear@24: // invalidate facebuffer and count nuclear@24: delete [] facebuf; nuclear@24: facebuf = 0; nuclear@24: num_faces = -1; nuclear@24: nuclear@13: return true; nuclear@13: } nuclear@13: John@14: int Scene::get_num_meshes() const John@14: { John@14: return (int)meshes.size(); John@14: } John@14: nuclear@13: int Scene::get_num_faces() const nuclear@13: { nuclear@24: if(num_faces >= 0) { nuclear@24: return num_faces; nuclear@24: } nuclear@24: nuclear@24: num_faces = 0; nuclear@13: for(size_t i=0; ifaces.size(); nuclear@13: } nuclear@13: return num_faces; nuclear@13: } nuclear@13: John@14: int Scene::get_num_materials() const John@14: { John@14: return (int)matlib.size(); John@14: } John@14: John@14: Material *Scene::get_materials() John@14: { John@14: if(matlib.empty()) { John@14: return 0; John@14: } John@14: return &matlib[0]; John@14: } John@14: John@14: const Material *Scene::get_materials() const John@14: { John@14: if(matlib.empty()) { John@14: return 0; John@14: } John@14: return &matlib[0]; John@14: } nuclear@24: nuclear@24: const Face *Scene::get_face_buffer() const nuclear@24: { nuclear@24: if(facebuf) { nuclear@24: return facebuf; nuclear@24: } nuclear@24: nuclear@24: int num_meshes = get_num_meshes(); nuclear@24: nuclear@24: printf("constructing face buffer with %d faces (out of %d meshes)\n", get_num_faces(), num_meshes); nuclear@24: facebuf = new Face[num_faces]; nuclear@24: Face *fptr = facebuf; nuclear@24: nuclear@24: for(int i=0; ifaces.size(); j++) { nuclear@24: *fptr++ = meshes[i]->faces[j]; nuclear@24: } nuclear@24: } nuclear@24: return facebuf; nuclear@24: } nuclear@24: nuclear@24: nuclear@24: void Scene::build_kdtree() nuclear@24: { nuclear@24: const Face *faces = get_face_buffer(); nuclear@24: int num_faces = get_num_faces(); nuclear@24: nuclear@25: printf("Constructing kd-tree out of %d faces ...\n", num_faces); nuclear@25: nuclear@25: free_kdtree(kdtree); nuclear@25: kdtree = new KDNode; nuclear@25: nuclear@25: /* Start the construction of the kdtree by adding all faces of the scene nuclear@25: * to the new root node. At the same time calculate the root's AABB. nuclear@25: */ nuclear@25: kdtree->aabb.min[0] = kdtree->aabb.min[1] = kdtree->aabb.min[2] = FLT_MAX; nuclear@25: kdtree->aabb.max[0] = kdtree->aabb.max[1] = kdtree->aabb.max[2] = -FLT_MAX; nuclear@25: nuclear@24: for(int i=0; iv[j].pos; nuclear@25: nuclear@25: // for each element (xyz) of the position vector ... nuclear@25: for(int k=0; k<3; k++) { nuclear@25: if(pos[k] < kdtree->aabb.min[k]) { nuclear@25: kdtree->aabb.min[k] = pos[k]; nuclear@25: } nuclear@25: if(pos[k] > kdtree->aabb.max[k]) { nuclear@25: kdtree->aabb.max[k] = pos[k]; nuclear@25: } nuclear@25: } nuclear@25: } nuclear@25: nuclear@25: kdtree->faces.push_back(face); // add the face nuclear@26: kdtree->num_faces++; nuclear@24: } nuclear@24: nuclear@26: // calculate the heuristic for the root nuclear@26: kdtree->cost = eval_cost(kdtree->faces, kdtree->aabb, kdtree->axis); nuclear@26: nuclear@25: // now proceed splitting the root recursively nuclear@25: ::build_kdtree(kdtree); nuclear@24: } nuclear@24: nuclear@25: static int build_kdtree(KDNode *kd) nuclear@24: { nuclear@26: int opt_max_items = accel_param[ACCEL_PARAM_MAX_NODE_ITEMS]; nuclear@26: if(kd->num_faces == 0 || (opt_max_items > 1 && kd->num_faces < opt_max_items)) { nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@26: int axis = (kd->axis + 1) % 3; nuclear@26: nuclear@26: float best_cost[2], best_sum_cost = FLT_MAX; nuclear@26: float best_split; nuclear@26: nuclear@26: std::list::iterator it = kd->faces.begin(); nuclear@26: while(it != kd->faces.end()) { nuclear@26: const Face *face = *it++; nuclear@26: nuclear@26: for(int i=0; i<3; i++) { nuclear@26: AABBox aabb_left, aabb_right; nuclear@26: const float *split = face->v[i].pos; nuclear@26: nuclear@26: aabb_left = aabb_right = kd->aabb; nuclear@26: aabb_left.max[axis] = split[axis]; nuclear@26: aabb_right.min[axis] = split[axis]; nuclear@26: nuclear@26: float left_cost = eval_cost(kd->faces, aabb_left, axis); nuclear@26: float right_cost = eval_cost(kd->faces, aabb_right, axis); nuclear@26: float sum_cost = left_cost + right_cost; nuclear@26: nuclear@26: if(sum_cost < best_sum_cost) { nuclear@26: best_cost[0] = left_cost; nuclear@26: best_cost[1] = right_cost; nuclear@26: best_sum_cost = sum_cost; nuclear@26: best_split = split[axis]; nuclear@26: } nuclear@26: } nuclear@26: } nuclear@26: nuclear@26: if(best_sum_cost >= kd->cost) { nuclear@26: return 0; // stop splitting if it doesn't reduce the cost nuclear@26: } nuclear@26: kd->pt = best_split; nuclear@26: nuclear@26: // create the two children nuclear@26: KDNode *kdleft, *kdright; nuclear@26: kdleft = new KDNode; nuclear@26: kdright = new KDNode; nuclear@26: nuclear@26: kdleft->axis = kdright->axis = axis; nuclear@26: kdleft->aabb = kdright->aabb = kd->aabb; nuclear@26: nuclear@26: kdleft->aabb.max[axis] = best_split; nuclear@26: kdright->aabb.min[axis] = best_split; nuclear@26: nuclear@26: kdleft->cost = best_cost[0]; nuclear@26: kdright->cost = best_cost[1]; nuclear@26: nuclear@26: it = kd->faces.begin(); nuclear@26: while(it != kd->faces.end()) { nuclear@26: const Face *face = *it++; nuclear@26: nuclear@26: if(face->v[0].pos[axis] < best_split || nuclear@26: face->v[1].pos[axis] < best_split || nuclear@26: face->v[2].pos[axis] < best_split) { nuclear@26: kdleft->faces.push_back(face); nuclear@26: kdleft->num_faces++; nuclear@26: } nuclear@26: if(face->v[0].pos[axis] >= best_split || nuclear@26: face->v[1].pos[axis] >= best_split || nuclear@26: face->v[2].pos[axis] >= best_split) { nuclear@26: kdright->faces.push_back(face); nuclear@26: kdright->num_faces++; nuclear@26: } nuclear@26: } nuclear@26: nuclear@26: kd->left = kdleft; nuclear@26: kd->right = kdright; nuclear@26: return 0; nuclear@26: } nuclear@26: nuclear@26: static float eval_cost(const std::list &faces, const AABBox &aabb, int axis) nuclear@26: { nuclear@26: int num_inside = 0; nuclear@26: int tcost = accel_param[ACCEL_PARAM_COST_TRAVERSE]; nuclear@26: int icost = accel_param[ACCEL_PARAM_COST_INTERSECT]; nuclear@26: nuclear@26: std::list::const_iterator it = faces.begin(); nuclear@26: while(it != faces.end()) { nuclear@26: const Face *face = *it++; nuclear@26: nuclear@26: for(int i=0; i<3; i++) { nuclear@26: if(face->v[i].pos[axis] >= aabb.min[axis] && face->v[i].pos[axis] < aabb.max[axis]) { nuclear@26: num_inside++; nuclear@26: break; nuclear@26: } nuclear@26: } nuclear@26: } nuclear@26: nuclear@26: return tcost + aabb.calc_surface_area() * num_inside * icost; nuclear@24: } nuclear@25: nuclear@25: static void free_kdtree(KDNode *node) nuclear@25: { nuclear@25: if(node) { nuclear@25: free_kdtree(node->left); nuclear@25: free_kdtree(node->right); nuclear@25: delete node; nuclear@25: } nuclear@25: }