#include #include #include #include #include #include //#define BSP_BUG "jmalbert" #undef BSP_BUG extern bool Debug_Tile; enum { Inside_Plane = 1, On_Plane = 2, Outside_Plane = 4, Split_Plane = Inside_Plane|Outside_Plane }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void FindPlanes( ElementRenderer::Element *element, Stuff::DynamicArrayOf *planes, int *planes_filled ) { Check_Object(element); Check_Object(planes); Check_Pointer(planes_filled); // //----------------------- // Spin through the group //----------------------- // if (element->IsDerivedFrom(ElementRenderer::GroupElement::DefaultData)) { ElementRenderer::GroupElement *group = Cast_Object(ElementRenderer::GroupElement *, element); Stuff::ChainIteratorOf *children = group->MakeIterator(); Register_Object(children); ElementRenderer::Element *child; while ((child = children->ReadAndNext()) != NULL) { Check_Object(child); FindPlanes(child, planes, planes_filled); } Unregister_Object(children); delete children; } // //---------------------- // Spin through the list //---------------------- // else if (element->IsDerivedFrom(ElementRenderer::ListElement::DefaultData)) { ElementRenderer::ListElement *list = Cast_Object(ElementRenderer::ListElement *, element); for (unsigned i=0; iGetActiveCount(); ++i) { ElementRenderer::Element *child = list->GetIndexedElement(i); Check_Object(child); FindPlanes(child, planes, planes_filled); } } // //----------------------- // Spin through the shape //----------------------- // else if (element->IsDerivedFrom(ElementRenderer::ShapeElement::DefaultData)) { ElementRenderer::ShapeElement *shape = Cast_Object(ElementRenderer::ShapeElement *, element); MidLevelRenderer::MLRShape *mlr_shape = shape->GetMLRShape(); Check_Object(mlr_shape); for (int p=0; pGetNum(); ++p) { MidLevelRenderer::MLRPrimitiveBase *primitive = mlr_shape->Find(p); Check_Object(primitive); // //---------------------------------------------------------------- // We have gotten the given primitive, now we need to loop through // the faces //---------------------------------------------------------------- // const Stuff::Point3D *points; int point_count; primitive->GetCoordData(&points, &point_count); MidLevelRenderer::MLRIndexedPrimitiveBase *indexed_primitive = Cast_Object( MidLevelRenderer::MLRIndexedPrimitiveBase *, primitive ); const unsigned short *indices; int index_count; indexed_primitive->GetIndexData(&indices, &index_count); Verify(!(index_count%3)); int face_count = index_count/3; Verify(face_count == primitive->GetNumPrimitives()); // //---------------------------------------------------------------- // We have gotten the given primitive, now we need to loop through // the faces and get the points of each triangle //---------------------------------------------------------------- // int index=0; for (int f=0; f(indices[index]) < point_count); const Stuff::Point3D *point1 = &points[indices[index++]]; Verify(index < index_count); Verify(static_cast(indices[index]) < point_count); const Stuff::Point3D *point2 = &points[indices[index++]]; Verify(index < index_count); Verify(static_cast(indices[index]) < point_count); const Stuff::Point3D *point3 = &points[indices[index++]]; #if defined(BSP_BUG) if (Debug_Tile) { SPEW(( BSP_BUG, "Tri %d\tv0=<%f,%f,%f>", f, point1->x, point1->y, point1->z )); SPEW(( BSP_BUG, "\t v1=<%f,%f,%f>", point2->x, point2->y, point2->z )); SPEW(( BSP_BUG, "\t v1=<%f,%f,%f>", point3->x, point3->y, point3->z )); } #endif // //--------------------------------------------- // Figure out the plane equation of the polygon //--------------------------------------------- // WORD plane = (*planes_filled)++; (*planes)[plane].BuildPlane( *point1, *point2, *point3 ); Verify((*planes)[plane].normal.y > Stuff::SMALL); // //----------------------------------------------------------- // Now figure out the plane equation of a vertical plane thru // each edge //----------------------------------------------------------- // Stuff::Point3D temp = *point1; temp.y += 10.0f; (*planes)[(*planes_filled)++].BuildPlane( *point1, *point2, temp ); (*planes)[(*planes_filled)++].BuildPlane( *point1, temp, *point3 ); temp = *point2; temp.y += 10.0f; (*planes)[(*planes_filled)++].BuildPlane( temp, *point2, *point3 ); } } } else STOP(("Didn't handle this case")); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void TestTriangles( ElementRenderer::Element *element, Stuff::DynamicArrayOf &planes, Stuff::DynamicArrayOf *scoreboard, int *triangles_tested ) { Check_Object(element); Check_Object(&planes); Check_Object(scoreboard); Check_Pointer(triangles_tested); // //----------------------- // Spin through the group //----------------------- // if (element->IsDerivedFrom(ElementRenderer::GroupElement::DefaultData)) { ElementRenderer::GroupElement *group = Cast_Object(ElementRenderer::GroupElement *, element); Stuff::ChainIteratorOf *children = group->MakeIterator(); Register_Object(children); ElementRenderer::Element *child; while ((child = children->ReadAndNext()) != NULL) { Check_Object(child); TestTriangles(child, planes, scoreboard, triangles_tested); } Unregister_Object(children); delete children; } // //---------------------- // Spin through the list //---------------------- // else if (element->IsDerivedFrom(ElementRenderer::ListElement::DefaultData)) { ElementRenderer::ListElement *list = Cast_Object(ElementRenderer::ListElement *, element); for (unsigned i=0; iGetActiveCount(); ++i) { ElementRenderer::Element *child = list->GetIndexedElement(i); Check_Object(child); TestTriangles(child, planes, scoreboard, triangles_tested); } } // //----------------------- // Spin through the shape //----------------------- // else if (element->IsDerivedFrom(ElementRenderer::ShapeElement::DefaultData)) { ElementRenderer::ShapeElement *shape = Cast_Object(ElementRenderer::ShapeElement *, element); MidLevelRenderer::MLRShape *mlr_shape = shape->GetMLRShape(); Check_Object(mlr_shape); for (int p=0; pGetNum(); ++p) { MidLevelRenderer::MLRPrimitiveBase *primitive = mlr_shape->Find(p); Check_Object(primitive); // //---------------------------------------------------------------- // We have gotten the given primitive, now we need to loop through // the faces //---------------------------------------------------------------- // const Stuff::Point3D *points; int point_count; primitive->GetCoordData(&points, &point_count); MidLevelRenderer::MLRIndexedPrimitiveBase *indexed_primitive = Cast_Object( MidLevelRenderer::MLRIndexedPrimitiveBase *, primitive ); const unsigned short *indices; int index_count; indexed_primitive->GetIndexData(&indices, &index_count); Verify(!(index_count%3)); int face_count = index_count/3; Verify(face_count == primitive->GetNumPrimitives()); // //---------------------------------------------------------------- // We have gotten the given primitive, now we need to loop through // the faces and get the points of each triangle //---------------------------------------------------------------- // int index=0; int score_index = *triangles_tested * planes.GetLength(); for (int f=0; f(indices[index]) < point_count); const Stuff::Point3D *point1 = &points[indices[index++]]; Verify(index < index_count); Verify(static_cast(indices[index]) < point_count); const Stuff::Point3D *point2 = &points[indices[index++]]; Verify(index < index_count); Verify(static_cast(indices[index]) < point_count); const Stuff::Point3D *point3 = &points[indices[index++]]; #if defined(BSP_BUG) if (Debug_Tile) SPEW((BSP_BUG, "Tri %d\t+", f)); #endif // //----------------------------------------------------- // Now we need to test these points against every plane //----------------------------------------------------- // for (int t=0; t 0.02) entry = Outside_Plane; else if (dist < -0.02) entry = Inside_Plane; else entry = On_Plane; dist = planes[t].GetDistanceTo(*point2); if (dist > 0.02) entry |= Outside_Plane; else if (dist < -0.02) entry |= Inside_Plane; else entry |= On_Plane; dist = planes[t].GetDistanceTo(*point3); if (dist > 0.02) entry |= Outside_Plane; else if (dist < -0.02) entry |= Inside_Plane; else entry |= On_Plane; // //--------------------------------------------------------- // If we are on both sides of the plane, count us as on the // plane //--------------------------------------------------------- // if ((entry&Split_Plane) == Split_Plane) entry |= On_Plane; Verify(entry != On_Plane || !(t&3)); #if defined(BSP_BUG) if (Debug_Tile) if ((t&3) == 3) SPEW((BSP_BUG, "%d +", entry)); else SPEW((BSP_BUG, "%d+", entry)); #endif } #if defined(BSP_BUG) if (Debug_Tile) SPEW((BSP_BUG, "")); #endif } } } else STOP(("Didn't handle this case")); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // float ComputeScore( int outsides, int insides, int splits, int total ) { // //----------------------------------------------------------------------- // The plane is useless if no triangles are reduced on either side of the // plane, or if this is a real polygon plane and it splits another // polygon //----------------------------------------------------------------------- // float score; if ( total == outsides || total == insides || (splits>0 && total>outsides+insides-splits) ) score = 3.0f * total; // //--------------------------------------------------------------------- // Otherwise, base the score on the sum of the child triangle count and // how balanced it is, giving a slight bias towards load over balance //--------------------------------------------------------------------- // else { float balance = Stuff::Fabs(static_cast(outsides - insides)); score = outsides + insides + 0.9f*balance; } #if defined(BSP_BUG) if (Debug_Tile) SPEW(( BSP_BUG, "o:%d i:%d s:%d -> %f", outsides, insides, splits, score )); #endif return score; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void SortTriangles( Stuff::DynamicArrayOf *bsp, WORD *bsp_count, Stuff::DynamicArrayOf *used_planes, WORD *used_plane_count, Stuff::DynamicArrayOf &scoreboard, Stuff::DynamicArrayOf &reference_planes, Stuff::DynamicArrayOf &triangles ) { Check_Object(bsp); Check_Pointer(bsp_count); Check_Object(&scoreboard); Check_Object(&reference_planes); Check_Object(&triangles); // //--------------------------------------------------------------------- // Our first task is to compile the statistics for each of the possible // splitting planes //--------------------------------------------------------------------- // int plane_count = reference_planes.GetLength(); Stuff::DynamicArrayOf insides(plane_count), outsides(plane_count), splits(plane_count); unsigned triangle_count = triangles.GetLength(); int p,t; #if defined(BSP_BUG) if (Debug_Tile) { SPEW((BSP_BUG, "BSP %d:", *bsp_count)); SPEW((BSP_BUG, "\t+")); for (t=0; t +", p)); #endif float score = ComputeScore(outsides[p], insides[p], splits[p], triangle_count); if (score < best_score) { best_plane = p; best_score = score; b_o = outsides[p]; b_i = insides[p]; b_s = splits[p]; } } #if defined(BSP_BUG) if (Debug_Tile) SPEW(( BSP_BUG, "\t%d -> O:%d I:%d S:%d = %f ====", best_plane, b_o, b_i, b_s, best_score )); #endif // //----------------------------------------------------- // We couldn't sort the rest so best plane will be zero //----------------------------------------------------- // if (best_plane < 0) { best_plane = 0; WORD new_plane; for (new_plane=0; new_plane<*used_plane_count; ++new_plane) { if ( !memcmp( &(*used_planes)[new_plane], &reference_planes[best_plane], sizeof(Stuff::Plane) ) ) break; } if (new_plane == *used_plane_count) { new_plane = (*used_plane_count)++; (*used_planes)[new_plane] = reference_planes[best_plane]; } int new_bsp = (*bsp_count)++; (*bsp)[new_bsp].m_planeIndex = new_plane; (*bsp)[new_bsp].m_innerIndex = 0; (*bsp)[new_bsp].m_outerIndex = 0; return; } Verify(best_plane>=0 && best_plane inside_triangles(inside_count); inside_count = 0; int outside_count = outsides[best_plane]; Stuff::DynamicArrayOf outside_triangles(outside_count); outside_count = 0; for (t=0; t 0) { (*bsp)[new_bsp].m_innerIndex = *bsp_count; #if defined(BSP_BUG) if (Debug_Tile) SPEW((BSP_BUG, "\tBSP %d.innerIndex = %d", new_bsp, *bsp_count)); #endif SortTriangles( bsp, bsp_count, used_planes, used_plane_count, scoreboard, reference_planes, inside_triangles ); } else { Verify(outside_count < triangles.GetLength()); Verify(!(best_plane&3)); } // //-------------------------- // Recurse the outside branch //-------------------------- // if (outside_count > 0) { (*bsp)[new_bsp].m_outerIndex = *bsp_count; #if defined(BSP_BUG) if (Debug_Tile) SPEW((BSP_BUG, "\tBSP %d.outerIndex = %d", new_bsp, *bsp_count)); #endif SortTriangles( bsp, bsp_count, used_planes, used_plane_count, scoreboard, reference_planes, outside_triangles ); } else { Verify(inside_count < triangles.GetLength()); Verify(!(best_plane&3)); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MakeBSP( Stuff::DynamicArrayOf *planes, WORD *plane_count, ElementRenderer::GroupElement *tile, Stuff::MemoryStream *stream ) { Check_Object(tile); Check_Object(stream); // //------------------------------------------------------------------------ // Count the number of triangles in the tile. We will end up computing // four planes per triangle, one for the face, and one vertical plane thru // each edge //------------------------------------------------------------------------ // int tri_count = tile->CountTriangles(); int ref_plane_count = 4*tri_count; Stuff::DynamicArrayOf reference_planes(ref_plane_count); int planes_filled = 0; FindPlanes(tile, &reference_planes, &planes_filled); Verify(planes_filled == ref_plane_count); // //----------------------------------------------------------------------- // We now need to fill the scoreboard up with the poly to plane test data //----------------------------------------------------------------------- // int total_tests = tri_count * ref_plane_count; Stuff::DynamicArrayOf scoreboard(total_tests); int triangles_tested = 0; TestTriangles(tile, reference_planes, &scoreboard, &triangles_tested); Verify(triangles_tested = tri_count); // //------------------------------------------------------------------------ // Now build the triangle index list in order to prime the sorting process //------------------------------------------------------------------------ // Stuff::DynamicArrayOf triangles(tri_count); for (int t=0; t bsp(ref_plane_count); WORD bsp_count = 0; // //--------------- // Sort the puppy //--------------- // SortTriangles( &bsp, &bsp_count, planes, plane_count, scoreboard, reference_planes, triangles ); Verify(bsp_count < bsp.GetLength()); // //------------------- // Write out the data //------------------- // *stream << bsp_count; stream->WriteBytes( bsp.GetData(), bsp_count*sizeof(Adept::TerrainBSP) ); }