Beispiel #1
0
TriangleList getSurroundingTriangles(QPointF *points, quint32 size, quint32 layers,
Vertex v, QPointF *centerPtr) {
	TriangleList result;
	if (!v.layer) {
		for (quint32 i = 0; i < size; ++i)
			result.append(TRIANGLE(0, i, 0));
		return result;
	}
	// inner part
	result.append(TRIANGLE(v.layer - 1, v.sector, 2 * v.index));
	if (v.index) {
		result.append(TRIANGLE(v.layer - 1, v.sector, 2 * v.index - 1));
		result.append(TRIANGLE(v.layer - 1, v.sector, 2 * v.index - 2));
	} else
		result.append(TRIANGLE(v.layer - 1, (v.sector + size - 1) % size, 2 * v.layer - 2));
	// outer part
	if (v.layer < layers) {
		result.append(TRIANGLE(v.layer, v.sector, 2 * v.index));
		result.append(TRIANGLE(v.layer, v.sector, 2 * v.index + 1));
		if (v.index) {
			result.append(TRIANGLE(v.layer, v.sector, 2 * v.index - 1));
		} else {
			result.append(TRIANGLE(v.layer, (v.sector + size - 1) % size, 2 * v.layer - 1));
			result.append(TRIANGLE(v.layer, (v.sector + size - 1) % size, 2 * v.layer));
		}
	}
	return result;
}
// groupedSliders are in the upper set of sliders
void TriangleDensityWidget::groupedSliderChangedSlot(int value)
{
	TriangleList triangles = triangleScene->triangles();
	double norm = getNorm();
	triangles.at(grouped_slider_idx)->xform()->density
		= (double)value / 1000. * norm;
	resetSliders(norm);
	emit dataChanged();
}
Beispiel #3
0
double phiFunction(QPointF *points, quint32 size, quint32 layers,
QPointF point, quint32 vertexNumber, QPointF *centerPtr) {
	Vertex vertex = getVertex(size, vertexNumber);
	TriangleList triangles = getSurroundingTriangles(points, size,
		layers, vertex, centerPtr);
	for (int i = 0; i < triangles.size(); ++i) {
		if (pointInTriangle(point, triangles.at(i)))
			return phiFunction(point, triangles.at(i),
			getPoint(points, size, layers, vertex, centerPtr));
	}
	return 0;
}
Beispiel #4
0
Deform::Deform(const float *P_data, int P_Num, const AdjList &adj_list, const TriangleList &triangle_list)
    :at(ORIGIN_HARD),
    P_data(P_data), 
    P_Num(P_Num), 
    max_iter(10), 
    min_tol(1e-3), 
    lambda(5), 
    adj_list(adj_list)
{

    for (size_t i = 0, i_end = triangle_list.size(); i < i_end; ++ i)
    {
        std::vector<int> face = triangle_list[i];
        std::sort(face.begin(), face.end());
        assert(face.size() == 3);
        face_list.push_back(Eigen::Vector3i(face[0], face[1], face[2]));
    }

    P.resize(3, P_Num);
    for (int i = 0; i != P_Num; ++i) {P.col(i) << P_data[3*i], P_data[3*i+1], P_data[3*i+2];}
    P_Prime = P;
    R = vector<Matrix3f>(P_Num, Matrix3f::Identity());

    // Weight
    build_weight_matrix();
}
Beispiel #5
0
void MeshMender::FindNeighbors(Triangle* tri,
				   TriangleList&possibleNeighbors,
				   Triangle** neighbor1,
				   Triangle** neighbor2,
				   std::vector< Vertex >& theVerts)
{
	*neighbor1 = NULL;
	*neighbor2 = NULL;

	std::vector<Triangle*> theNeighbors;
	for(unsigned int n = 0; n < possibleNeighbors.size(); ++n)
	{
		TriID tID = possibleNeighbors[n];
		Triangle* possible =&(m_Triangles[ tID]);
		if(possible != tri ) //check for myself
		{
			if( SharesEdge(tri, possible, theVerts)  )
			{
				theNeighbors.push_back(possible);

			}
		}
	}

	if(theNeighbors.size()>0)
		*neighbor1 = theNeighbors[0];
	if(theNeighbors.size()>1)
		*neighbor2 = theNeighbors[1];
}
Beispiel #6
0
  void KDTree::generateEventList( TriangleList& triList, AABB& parentBox )
  {
    //Start with an empty event list
    eventList.clear();

    //Go through all the triangles passed in.
    for(unsigned i = 0; i < triList.size(); ++i)
    {
      //Clip the triangle to the parent box
      Triangle tri = triList[i];
      clipTriToParentBox(tri, parentBox);

      //And the box be the AABB of this event
      AABB* pBox = &parentBox;

      //Grab the minimum and maximum points of the triangle as per this split axis
      float pMin = GeometryLibrary::minPos(tri, curAxis);
      float pMax = GeometryLibrary::maxPos(tri, curAxis);

      //If the maximum and minimum points are the same, we have a coplanar event
      if(pMax == pMin)
        eventList.push_back(KDEvent(Coplanar, &tri, pMin, pBox));
      //Otherwise start at the minimum and end at the maximum on this axis
      else
      {
        eventList.push_back(KDEvent(Starting, &tri, pMin, pBox));
        eventList.push_back(KDEvent(Ending, &tri, pMax, pBox));
      }
    }

    //Sort according to the operator < provided so that the events are in ascending order
    std::sort(eventList.begin(), eventList.end());
  }
Beispiel #7
0
bool Triangulator::check_triangle(Vertex *first, TriangleList &triangles) {
	// path is a dot
	if (first->next == first)
		return true;

	// path is a two lines
	if (first->next->next == first)
		return true;

	// path is triangle
	if (first->next->next->next == first) {
		triangles.push_back(first->index);
		triangles.push_back(first->next->index);
		triangles.push_back(first->next->next->index);
		return true;
	}

	return false;
}
void TriangleDensityWidget::reset()
{
	TriangleList triangles = triangleScene->triangles();

	// make more sliders if necessary
	while (sliders.size() < triangles.size())
	{
		GroupedSlider* s
			= new GroupedSlider(Qt::Horizontal, this, sliders.size());
		QLabel* name = new QLabel(QString::number(sliders.size() + 1));
		QHBoxLayout* hl = new QHBoxLayout();
		hl->addWidget(name);
		hl->addWidget(s);
		dynamic_cast<QVBoxLayout*>
			(m_scrollAreaWidgetContents->layout())->insertLayout(-1,hl,0);
		s->setRange(0, 1000);
		s->setVisible(false);
		sliders.append(s);
		slider_names.append(name);
		connect(s, SIGNAL(valueChanged(int)), this, SLOT(groupedSliderChangedSlot(int)));
		connect(s, SIGNAL(sliderPressed()), this, SLOT(groupedSliderPressedSlot()));
		connect(s, SIGNAL(undoStateSignal()), this, SIGNAL(undoStateSignal()));
	}
	// hide non-used sliders
	for (int n = triangles.size() ; n < sliders.size() ; n++)
	{
		sliders.at(n)->setVisible(false);
		slider_names.at(n)->setVisible(false);
	}
	resetSliders(getNorm());

	QStringList items;
	items << tr("None");
	for (int n = 1 ; n <= genome->size() ; n++)
		items << QString::number(n);
	int n = m_crossComboBox->currentIndex();
	m_crossComboBox->blockSignals(true);
	m_crossComboBox->clear();
	m_crossComboBox->addItems(items);
	m_crossComboBox->setCurrentIndex(n);
	m_crossComboBox->blockSignals(false);
}
Beispiel #9
0
void triangle_tree(
  const Eigen::MatrixXd & V,
  const Eigen::MatrixXi & F,
  TriTree & tree,
  TriangleList & tlist)
{
  assert(F.cols() == 3);
  tlist.clear();

  // Loop over facets
  for(int f = 0;f<F.rows();f++)
  {
    Point3 a(V(F(f,0),0), V(F(f,0),1), V(F(f,0),2));
    Point3 b(V(F(f,1),0), V(F(f,1),1), V(F(f,1),2));
    Point3 c(V(F(f,2),0), V(F(f,2),1), V(F(f,2),2));
    tlist.push_back(Triangle3( a,b,c));
  }
  // constructs AABB tree
  tree.clear();
  tree.insert(tlist.begin(),tlist.end());
}
Beispiel #10
0
  void KDTree::splitTriList( TriangleList& l, TriangleList& r, TriangleList& p, float pos )
  {
    curAxis = splitAxis;
    unsigned left, right, coplanar, side;

    //Go through every triangle in the triangle list
    for(unsigned i = 0; i < p.size(); ++i)
    {
      Triangle& tri = p[i];
      left = right = coplanar = 0;
      CountVertPositions(tri, left, right, coplanar, splitAxis, pos);

      side = max(left, max(right, coplanar));

      if(side == left)
        l.push_back(tri);
      else if(side == right)
        r.push_back(tri);
      else if(side == coplanar)
        l.push_back(tri);
    }
  }
Beispiel #11
0
  void KDTree::findMinPlane(TriangleList& triList, AABB& parentBox)
  {
    unsigned Nl, Nr, Nc, Vs, Ve, Vc;
    Vs = Ve = Vc = 0;

    //Set minimum cost to infinite
    minCost = FLT_MAX;

    //For every possible axis,
    for(unsigned i = 0; i < NumAxes; ++i)
    {
      //Generate the event list for this axis
      curAxis = (SplitAxis)i;
      generateEventList(triList, parentBox);

      //All of the triangles are to our right.
      //These will remain through iterations on positions
      Nl = 0;
      Nr = triList.size();
      Nc = 0;

      //For every position
      for(unsigned j = 0; j < eventList.size(); )
      {
        float curPos = eventList[j].eventPos;
        Vs = Ve = Vc = 0;
        //Count the triangles starting, ending, and coplanar with this position
        //This will advance j to the next event position
        countTrisOfEvent(Vs, Ve, Vc, j, curPos);

        Nc  = Vc;
        Nr -= Ve + Vc;

        //The last index is the positions we care about.
        calcCostOfPlane(Nl, Nr, Nc, parentBox, curPos);

        Nl += Vs + Vc;

        //Update the lowest possible plane
        if(curCost < minCost)
        {
          minCost = curCost;

          //Save what axis and position we're going to cut at so far...
          splitPos = curPos;
          splitAxis = curAxis;
        }
      }
    }
  }
Beispiel #12
0
inline unsigned numSurfaceIntersections(const V3 &voxelPos, const V3 &dir)
{
    unsigned numHits = 0;
    
    /********* ASSIGNMENT *********/
    /* Check and return the number of times a ray cast in direction dir,
     * from voxel center voxelPos intersects the surface */
    CompFab::Ray r;
    for (int i = 0; i < triangleList.size(); i++) {
        r = CompFab::Ray(voxelPos, dir);
        if (rayTriangleIntersection(r, triangleList[i])) {
            numHits += 1;
        }
    }
    return numHits;
}
Beispiel #13
0
void MeshMender::UpdateIndices(const size_t oldIndex , const size_t newIndex , TriangleList& curGroup)
{
   //make any triangle that used the oldIndex use the newIndex instead

	for( size_t t = 0; t < curGroup.size(); ++t )
	{
		TriID tID = curGroup[ t ];
		for(size_t indx = 0 ; indx < 3 ; ++indx)
		{
			if(m_Triangles[tID].indices[indx] == oldIndex)
			{
				m_Triangles[tID].indices[indx] = newIndex;
			}
		}
	}

}
Beispiel #14
0
void calculateSplittingOfMesh() {
    
    //need to change dividing by also
    const unsigned dim = 32; //dimension of voxel grid (e.g. 32x32x32)
    int mass[dim] = {};
    int voxcount[dim] = {};
    float totalcount = 0;
    float massheight = 0;
    float totalmass = 0;
    int com[dim] = {};
    int average = 0;
    int cut1 = 0;
    int cut2 = 0;
    int pointer = 0;
    
    CompFab::Mesh tempMesh("bunny.obj", true);
    
    triangleList.resize(tempMesh.t.size());
    
    //copy triangles to global list
    for (unsigned i = 0; i < tempMesh.t.size(); ++i) {
        triangleList[i].v0 = tempMesh.v[tempMesh.t[i][0]];
        triangleList[i].v1 = tempMesh.v[tempMesh.t[i][1]];
        triangleList[i].v2 = tempMesh.v[tempMesh.t[i][2]];
    }
    
    //Create Voxel Grid
    V3 bbMax, bbMin;
    BBox(tempMesh, bbMin, bbMax);
    
    //Build Voxel Grid
    Real bbX = bbMax[0] - bbMin[0];
    Real bbY = bbMax[1] - bbMin[1];
    Real bbZ = bbMax[2] - bbMin[2];
    Real spacing;
    
    if (bbX > bbY && bbX > bbZ) {
        spacing = bbX / (Real)(dim - 2);
    } else if (bbY > bbX && bbY > bbZ) {
        spacing = bbY / (Real)(dim - 2);
    } else {
        spacing = bbZ / (Real)(dim - 2);
    }
    
    V3 hspacing(0.5 * spacing);

    voxelGrid = CompFab::VoxelGrid(bbMin - hspacing, dim, dim, dim, spacing);
    
    
    
    V3 voxelPos;
    V3 direction(1, 0, 0);
    /********* ASSIGNMENT *********/
    /* Iterate over all voxels in voxelGrid and test whether they are inside our outside of the
     * surface defined by the triangles in triangleList */
    for (int i = 0; i < dim; i++) {
        for (int k = 0; k < dim; k++) {
            for (int j = 0; j < dim; j++) {
                //find i-voxel
                V3 voxeli = V3(i * voxelGrid.spacing, 0, 0);
                //find j-voxel
                V3 voxelj = V3(0, j * voxelGrid.spacing, 0);
                //find k-voxel
                V3 voxelk = V3(0, 0, k * voxelGrid.spacing);
                //find the new position from iteration, start from origin then add each dimension
                voxelPos = voxelGrid.lowerLeft + voxeli + voxelj + voxelk;
                voxelGrid.isInside(i, j, k) = numSurfaceIntersections(voxelPos, direction) % 2;
                if (voxelGrid.isInside(i, j, k)) { mass[j]++; voxcount[j]++;}
            }
        }
    }
    //compilation of all COM after each layer is change to 0.2 mass
    
    for (int p = dim-1; p > 0-1; p--) {
        mass[p] = 0.2*mass[p];
        for (int q = 0; q < dim; q++) {
            totalmass = totalmass + mass[q];
            massheight = massheight + (mass[q] * q);
        }
        com[p]= ceil(massheight / totalmass);
        //printf("COM%d is %d\n", p, com[p]);
        totalmass = 0;
        massheight = 0;
    }
    int cut = dim - 1;
    for (int p = dim - 1; p > 0; p--) {
        if (com[cut] >= com[p]) {
            cut = p;
        }
    }
    
    for (int i = 0; i < dim; i++) {
        totalcount = totalcount + voxcount[i];
        printf("Layer%d voxcount=%d\n", i, voxcount[i]);
    }
    
    // sorts in ascending order
    std::sort(std::begin(voxcount), std::end(voxcount));
    
    //percentile calculation
    pointer = 0.25 * dim;
    if (floor(pointer) == pointer)
        average = (voxcount[pointer] + voxcount[pointer + 1]) * 0.5;
    else
        average = voxcount[(int) ceil(pointer)];
    
    //printf("cut@ %d, threshold=%d\n", cut,average);
    
    
    for (int q = cut; q < dim; q++) {
        cut1 = q;
        if (voxcount[q] > average) {
            break;
        }
    }
    for (int r = cut; r > 0; r--) {
        cut2 = r;
        if (voxcount[r] > average) {
            break;
        }
    }
    printf("lowestCOM cut@Layer%d with count%d. Threshold=%d therefore cut1@Layer%d with count%d cut2@Layer%d with count%d\n", cut,voxcount[cut],average,cut1,voxcount[cut1],cut2,voxcount[cut2]);
    
    cutPoint1 = cut1;
    cutPoint2 = cut2;
    
    inputMeshToCorkMesh();
}
Beispiel #15
0
void Triangulator::triangulate(const Contour &contour, TriangleList &triangles, int index_offset) {
	Path path;
	build_path(contour, path, index_offset);
	triangles.reserve(triangles.size() + 3*(path.size() - 2));
	split_path(&path.front(), triangles);
}
Beispiel #16
0
TriangleList getCommonTriangles(QPointF *points, quint32 size, quint32 layers,
Vertex v1, Vertex v2, QPointF *centerPtr) {
	TriangleList result;
	if (v1.layer > v2.layer)
		qSwap(v1, v2);
	if (!v2.layer)
		return result;
	if (v1.layer == 0 && v2.layer == 1) {
		result.append(TRIANGLE(0, v2.sector, 0));
		result.append(TRIANGLE(0, (v2.sector + size - 1) % size, 0));
	} else if (v1.sector == v2.sector) {
		if (v1.layer == v2.layer) {
			if (v2.index == v1.index + 1) {
				if (v1.layer < layers)
					result.append(TRIANGLE(v1.layer, v1.sector, v1.index * 2 + 1));
				result.append(TRIANGLE(v1.layer - 1, v1.sector, v1.index * 2));
			} else if (v2.index == v1.index - 1) {
				if (v1.layer < layers)
					result.append(TRIANGLE(v1.layer, v1.sector, v2.index * 2 + 1));
				result.append(TRIANGLE(v1.layer - 1, v1.sector, v2.index * 2));
			}
		}
		else if (v2.layer == v1.layer + 1) {
			if (v2.index == v1.index) {
				if (v1.index)
					result.append(TRIANGLE(v1.layer, v1.sector, v1.index * 2 - 1));
				else
					result.append(TRIANGLE(v1.layer, (v1.sector + size - 1) % size, v1.layer * 2));
				result.append(TRIANGLE(v1.layer, v1.sector, v1.index * 2));
			} else if (v2.index == v1.index + 1) {
				result.append(TRIANGLE(v1.layer, v1.sector, v1.index * 2));
				result.append(TRIANGLE(v1.layer, v1.sector, v1.index * 2 + 1));
			}
		}
	} else if ((v2.sector == (v1.sector + 1) % size)
	&& v1.index == v1.layer - 1 && v2.index == 0) {
		if (v1.layer == v2.layer) {
			result.append(TRIANGLE(v1.layer - 1, v1.sector, v1.index * 2));
			if (v1.layer < layers)
				result.append(TRIANGLE(v1.layer, v1.sector, v1.index * 2 + 1));
		} else if (v1.layer == v2.layer + 1) {
			result.append(TRIANGLE(v2.layer, v1.sector, v1.index * 2 - 1));
			result.append(TRIANGLE(v2.layer, v1.sector, v1.index * 2));
		}
	} else if ((v1.sector == (v2.sector + 1) % size)
	&& v2.index == v2.layer - 1 && v1.index == 0) {
		if (v1.layer == v2.layer) {
			result.append(TRIANGLE(v1.layer - 1, v2.sector, v2.index * 2));
			if (v1.layer < layers)
				result.append(TRIANGLE(v1.layer, v2.sector, v2.index * 2 + 1));
		} else if (v1.layer == v2.layer - 1) {
			result.append(TRIANGLE(v1.layer, v2.sector, v2.index * 2 - 1));
			result.append(TRIANGLE(v1.layer, v2.sector, v2.index * 2));
		}
	}
	return result;
}
Beispiel #17
0
void KDTree::Subdivide(KDTreeNode &node,TriangleList *triangles,aabb& box,int depth)
{
	if(!triangles)return ;
	int count = triangles->GetCount();
	if( count < minTrianglesPerleafNode ||(depth>=20&&count<=maxTrianglesPerleafNode))
	{
		node.SetTriangleList(triangles);
		node.Type = LEFT;
		node.box = box;
		return ;
	}

	
	aabb frontBox,backBox;
	real splitPosition;
	bool foundOptimalSplit = findOptimalSplitPositon(triangles,box,depth,splitPosition,frontBox, backBox);

	if(foundOptimalSplit == true)
	{
		
		node.Type = PATITION;
		node.SetAxis(depth%Dimension);
		node.SetSplitPosition(splitPosition);
		node.box = box;
		

		TriangleList *frontTriangles = new TriangleList();
		TriangleList *backTriangles =	new TriangleList();
		for( int i=0; i < count; i++)
		{
			//Determine on which side of the split each
			//triangle belongs.
		    TriangleNode * triangle= triangles->GetHead();
			triangles->DeleteHead();
			
			int position = partitionTriangle(triangle,depth,splitPosition);
			triangle->next = NULL;
			switch(position)
			{
			case kdBefore:
				frontTriangles->append(triangle);
				break;
			case kdAfter:
				backTriangles->append(triangle);
				break;
			case kdIntersection:
				//frontTriangles->append(triangle);
				backTriangles->append(triangle);
				break;
			}
			
		}
		node.m_lchild = new KDTreeNode();
		node.m_rchild = new KDTreeNode();
		Subdivide(*node.m_lchild,frontTriangles,frontBox,depth+1);
		Subdivide(*node.m_rchild,backTriangles,backBox,depth+1);
	}else
	{
		
		node.SetTriangleList(triangles);
		node.Type = LEFT;
		node.box = box;
	}

}
Beispiel #18
0
void MeshMender::ProcessBinormals(TriangleList& possibleNeighbors,
								std::vector< Vertex >&    theVerts,
								std::vector< unsigned int >& mappingNewToOldVert,
								D3DXVECTOR3 workingPosition)
{
		NeighborGroupList neighborGroups;//a fresh group for each pass


		//reset each triangle to prepare for smoothing group building
		unsigned int i;
		for(i =0; i < possibleNeighbors.size(); ++i )
		{
			m_Triangles[ possibleNeighbors[i] ].Reset();
		}

		//now start building groups
		CanSmoothBinormalsChecker canSmoothBinormalsChecker;
		for(i =0; i < possibleNeighbors.size(); ++i )
		{
			Triangle* currTri = &(m_Triangles[ possibleNeighbors[i] ]);
			assert(currTri);
			if(!currTri->handled)
			{
				BuildGroups(currTri,possibleNeighbors,
							neighborGroups, theVerts,
							&canSmoothBinormalsChecker ,MinBinormalsCreaseCosAngle );
			}
		}


		std::vector<D3DXVECTOR3> groupBinormalVectors;


		for(i=0; i<neighborGroups.size(); ++i)
		{
			D3DXVECTOR3 gbinormal(0,0,0);
			for(unsigned int t = 0; t < neighborGroups[i].size(); ++t)//for each triangle in the group,
			{
				TriID tID = neighborGroups[i][t];
				gbinormal+=  m_Triangles[tID].binormal;
			}
			gbinormal = glm::normalize(gbinormal.GLMvec());//D3DXVec3Normalize( &gbinormal, &gbinormal );
			groupBinormalVectors.push_back(gbinormal);
		}

		//next step, ensure that triangles in different groups are not
		//sharing vertices. and give the shared vertex their new group vector
		std::set<size_t> otherGroupsIndices;
		for( i = 0; i < neighborGroups.size(); ++i )
		{
			TriangleList& curGroup = neighborGroups[ i ];
			std::set<size_t> thisGroupIndices;

			for( size_t t = 0; t < curGroup.size(); ++t ) //for each tri
			{
				TriID tID = curGroup[ t ];
				for(size_t indx = 0; indx < 3 ; ++indx)//for each vert in that tri
				{
					//if it is at the positions in question
					if( theVerts[ m_Triangles[tID].indices[indx] ].pos  == workingPosition)
					{
						//see if another group is already using this vert
						if(otherGroupsIndices.find( m_Triangles[tID].indices[indx] ) != otherGroupsIndices.end() )
						{
							//then we need to make a new vertex
							Vertex ov;
							ov = theVerts[ m_Triangles[tID].indices[indx] ];
							ov.binormal = groupBinormalVectors[i];
							size_t oldIndex = m_Triangles[tID].indices[indx];
							size_t newIndex = theVerts.size();
							theVerts.push_back(ov);
							AppendToMapping( oldIndex , m_originalNumVerts , mappingNewToOldVert);
							UpdateIndices(oldIndex,newIndex,curGroup);
						}
						else
						{
							//otherwise, just update it with the new vector
							theVerts[ m_Triangles[tID].indices[indx] ].binormal = groupBinormalVectors[i];
						}

						//store that we have used this index, so other groups can check
						thisGroupIndices.insert(m_Triangles[tID].indices[indx]);
					}
				}

			}

			for(std::set<size_t>::iterator it = thisGroupIndices.begin(); it!= thisGroupIndices.end() ; ++it)
			{
				otherGroupsIndices.insert(*it);
			}

		}

}
void GeometryEngine::initGeometry(TriangleList triangles, QVector<QVector3D> lines, QVector<QVector3D> points)
{
    isTransparent = false;

    initializeOpenGLFunctions();

    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Generate 2 VBOs
    arrayBuf.create();
    indexBuf.create();
    arrayBufLines.create();
    arrayBufPoints.create();

    std::vector<VertexData> vertices;
    std::vector<GLuint> indices;
    std::vector<GLuint> tmp(3);

    // iterate over all triangles
    for (uint i = 0; i < triangles.size(); i++) {
        // iterate over all vertices in triangle
        for (int j = 0; j < 3; j++) {
            // create vertex with texture coords
            VertexData vertex = {
                QVector3D(triangles[i][j][0], triangles[i][j][1], triangles[i][j][2]),
                QVector2D(abs(((j+1)%2)-1), abs(((j+1)%3)-1)) // tex coords (0,0),(1,1),(0,1)
            };

            // is vertex already in indices?
            GLuint idx = indexOf(vertices, vertex);
            if (idx >= vertices.size()) {
                // no, add it to the end of the list
                //idx = vertices.size();
                vertices.push_back(vertex);
            }
            // prime the index of current vertex for insertion
            tmp[j] = idx;
        }
        // insert vertex indices of current triangle
        indices.insert(indices.end(), tmp.begin(), tmp.end());

        // render both sides of triangles, for now as a #define option
#if TWO_SIDED
        std::reverse(tmp.begin(), tmp.end());
        indices.insert(indices.end(), tmp.begin(), tmp.end());
#endif
    }

#if DEBUG
    std::cout << "Vertices:" << std::endl << vertices.size() << std::endl;
    for (auto i = vertices.begin(); i != vertices.end(); ++i)
        std::cout << "(" << (*i).position[0] <<"," << (*i).position[1] <<"," << (*i).position[2] <<")" << ' ';
    std::cout << std::endl;

    std::cout << "Indices:" << std::endl << indices.size() << std::endl;
    for (auto i = indices.begin(); i != indices.end(); ++i)
        std::cout << *i << ' ';
    std::cout << std::endl;
#endif

    arrayBuf.bind();
    arrayBuf.allocate(&vertices[0], vertices.size() * sizeof(VertexData));

    indexBuf.bind();
    indexBuf.allocate(&indices[0], indices.size() * sizeof(GLuint));

    idxLen = indices.size();

    if (!lines.empty())
    {
        arrayBufLines.bind();
        arrayBufLines.allocate(&lines[0], lines.size() * sizeof(QVector3D));
    }

    if (!points.empty())
    {
        arrayBufPoints.bind();
        arrayBufPoints.allocate(&points[0], points.size() * sizeof(QVector3D));
    }
}
Beispiel #20
0
//------------------------------------------------------------------------------------------------
// Name:  XMesh
// Desc:  Constructs the subset geometry for a D3DXMesh
//------------------------------------------------------------------------------------------------
bool XMesh::buildGeometryFromD3DXMesh(LPD3DXMESH d3dxMesh, SubsetGeometry* subsetGeometry, DWORD subsets)
{
    // Check parameters
    if (APP_ERROR(!d3dxMesh || !subsetGeometry)("Invalid parameter to XMesh::buildGeometryFromD3DXMesh"))
        return false;

    // Add a reference to the mesh to counteract freeing it at the end
    d3dxMesh->AddRef();

    // Get the device
    LPDIRECT3DDEVICE9 pd3dDevice = NULL;
    d3dxMesh->GetDevice(&pd3dDevice);

    // If this mesh isn't already in the correct format, have D3D do the grunt work of
    // converting it.
    bool generate_normals = false; // Whether or not normals need to be generated for this mesh
    if ((d3dxMesh->GetFVF() != D3DFVF_GEOMETRYVERTEX) ||
        (D3DFMT_GEOMETRYINDEX == D3DFMT_INDEX32) && ((d3dxMesh->GetOptions() & D3DXMESH_32BIT) == 0))
    {
        // Holds the mesh when its converted to the correct format
        LPD3DXMESH pTemd3dxMesh = NULL;

        // Duplicate the loaded mesh into the format
        if (APP_ERROR(d3dxMesh->CloneMeshFVF(
                            D3DXMESH_SYSTEMMEM | ((D3DFMT_GEOMETRYINDEX == D3DFMT_INDEX32) ? D3DXMESH_32BIT : 0),
                            D3DFVF_GEOMETRYVERTEX, pd3dDevice, &pTemd3dxMesh))
                     ("XMesh couldn't convert the source geometry format")) {
            d3dxMesh->Release();
            pd3dDevice->Release();
			      return false;
        }

        // Generate normals if they didn't exist
        generate_normals = ((d3dxMesh->GetFVF()&D3DFVF_NORMAL)!=D3DFVF_NORMAL &&
                            (D3DFMT_GEOMETRYINDEX&D3DFVF_NORMAL)!=D3DFVF_NORMAL);

        // Use this mesh instead
        d3dxMesh->Release();
        d3dxMesh = pTemd3dxMesh;
    }

    // The mesh must have its attributes sorted before it can be converted to single strips
    {
        // Allocate an adjacency buffer
        DWORD faces = d3dxMesh->GetNumFaces();
        DWORD* pAdjacency = new DWORD[faces * 3];
		    bool failed = false;

        if (APP_ERROR(FAILED(d3dxMesh->GenerateAdjacency(ADJACENCY_EPSILON, pAdjacency)))("Unable to generate the mesh adjacency"))
          failed = true;

        { // Clean up "bowties" in the mesh that prevent lighting from being calculated correctly
          LPD3DXMESH cleaned_mesh = NULL;
          DWORD* cleaned_adjacency = new DWORD[faces * 3];
          LPD3DXBUFFER errors_and_warnings = NULL;
          if (!failed && APP_ERROR(FAILED(D3DXCleanMesh(D3DXCLEAN_BOWTIES,
                                                        d3dxMesh,
                                                        pAdjacency,
                                                        &cleaned_mesh,
                                                        cleaned_adjacency,
                                                        &errors_and_warnings)))
                                  ("Failed to clean mesh")) {
            failed = true;
            if (errors_and_warnings) {
              DEBUG_ERROR("Mesh cleaning error:  %s", (const char*)errors_and_warnings->GetBufferPointer());
            }
          }

          SAFE_RELEASE(errors_and_warnings);

          // If we successfully cleaned the mesh, use the new mesh and new set of
          // adjacencies.  Otherwise, just delete anything that was allocated and
          // keep the original.
          if (failed) {
            SAFE_DELETE_ARRAY(cleaned_adjacency);
            SAFE_RELEASE(cleaned_mesh);
          } else {
            SAFE_DELETE_ARRAY(pAdjacency);
            SAFE_RELEASE(d3dxMesh)
            pAdjacency = cleaned_adjacency;
            d3dxMesh = cleaned_mesh;
          }
        }

        // Compute mesh normals, if necessary
        if (!failed && generate_normals && APP_ERROR(FAILED(D3DXComputeNormals(d3dxMesh, pAdjacency)))("Couldn't generate mesh normals")) {
          failed = true;
        }

        // Optimize the mesh
        if (!failed && APP_ERROR(FAILED(d3dxMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT,
                                                                  pAdjacency,
                                                                  NULL,
                                                                  NULL,
                                                                  NULL)))
                                 ("Couldn't optimize mesh attributes")) {
			    failed = true;
		    }

        // Get rid of the temporary adjacency buffer
        SAFE_DELETE_ARRAY(pAdjacency);

        // Return if there was an error
        if (failed) {
          SAFE_RELEASE(d3dxMesh);
          SAFE_RELEASE(pd3dDevice);
          return false;
        }
    }

    // Lock the vertex buffer
    GeometryVertex* pXVertices = NULL;
    if (APP_ERROR(d3dxMesh->LockVertexBuffer(D3DLOCK_READONLY, (VOID**)&pXVertices))("Couldn't lock source vertex buffer"))
    {
		// Erase this mesh
        d3dxMesh->Release();
        pd3dDevice->Release();

		// Failure
		return false;
    }

    // Iterate through all of the materials and copy vertex/index data, and assign material
    // information for the mesh.
    for (DWORD subset = 0; subset < subsets; subset++)
    {
        // Use D3DX to convert this subset into a nicely indexed form
        DWORD numStripIndices;
        LPDIRECT3DINDEXBUFFER9 pSubsetIB;
        if (APP_ERROR(D3DXConvertMeshSubsetToSingleStrip(d3dxMesh, subset, D3DXMESH_SYSTEMMEM, &pSubsetIB, &numStripIndices))("Couldn't convert mesh subset into indexable strip"))
        {
            // Erase any geometry we made
            DeallocateGeometry(subsetGeometry);

            // Get rid of the mesh
            d3dxMesh->UnlockVertexBuffer();
            d3dxMesh->Release();

            // Free our device
            pd3dDevice->Release();

            // Return the error
            return false;
        }

        D3DINDEXBUFFER_DESC desc;
        GeometryIndex* pXIndices = NULL;

        // Check the format of the indices and lock the strip index buffer
        if (APP_ERROR(pSubsetIB->GetDesc(&desc))("Couldn't get .X mesh IB desc") || (desc.Format != D3DFMT_GEOMETRYINDEX) ||
            APP_ERROR(pSubsetIB->Lock(0, 0, (VOID**)&pXIndices, D3DLOCK_READONLY))("Unable to lock the .X index buffer"))
        {
            // Erase any geometry we made
            DeallocateGeometry(subsetGeometry);

            // Get rid of the mesh
            pSubsetIB->Release();
            d3dxMesh->UnlockVertexBuffer();
            d3dxMesh->Release();

            // Free our device
            pd3dDevice->Release();

            // Error!
            return false;
        }

        // This table pairs an index from the .X file to an index in the buffer that
        // holds the vertices for this subset
        XIndicesTable xIndicesTable;

        // For each of the indices in the strip, puts its vertex ID into the indices
        // table.  Use the counter to determine which vertex this is.
        {
            GeometryIndex vertexCounter = 0;
            for (DWORD e = 0; e < numStripIndices; ++e)
            {
                // Insert the entry [x-mesh index, subset index] into the table
                XIndicesTableInsertResult result = xIndicesTable.insert(XIndicesEntry(pXIndices[e], vertexCounter));

                // If the result was successful (this isn't a duplicated X-mesh index) increment the vertex counter
                if (result.second)
                    vertexCounter++;
            }
        }

        // Grab the number of vertices this geometry uses
        DWORD numVertices = (DWORD)xIndicesTable.size();

        // This buffer holds all of the triangles in this subset
        TriangleList triangles;

        // This list keeps track of locations in the strip where the winding order changes.  This is necessary
        // because this next part will remove degenerate triangles from the list.
        std::set<size_t> windingChanges;

        // Generate the list of triangles from the strip provided
        for (DWORD t = 0; t < numStripIndices - 2; ++t)
        {
            // Build the triangle that will be added to the buffer
            // CHANGED July 25, 2008:  the winding order is wrong here
            //Triangle tri = { pXIndices[t + 0], pXIndices[t + 1], pXIndices[t + 2] };
            Triangle tri = { pXIndices[t + 0], pXIndices[t + 2], pXIndices[t + 1] };



            // Convert the triangle into subset-indices by using the lookup table
            // we generated before.
            tri.index[0] = xIndicesTable.find(tri.index[0])->second;
            tri.index[1] = xIndicesTable.find(tri.index[1])->second;
            tri.index[2] = xIndicesTable.find(tri.index[2])->second;

            // Check to make sure this triangle isn't degenerate.  If it is, we can just skip
            // this triangle entirely to simplify the geometry.
            if (tri.index[0] == tri.index[1] || tri.index[1] == tri.index[2] || tri.index[0] == tri.index[2])
            {
                // Try to find the winding in the list
                std::set<size_t>::iterator currentWinding = windingChanges.find(triangles.size());

                // Add this to the winding change list, or remove the change if it's already there
                if (currentWinding != windingChanges.end())
                    windingChanges.erase(currentWinding);
                else
                    windingChanges.insert(triangles.size());

                // Don't insert a triangle here
                continue;
            }

            // Add this triangle to the list
            triangles.push_back(tri);
        }

        // Calculate the number of indices we need for the buffer
        DWORD numGeometryIndices = (DWORD)(triangles.size() * 3);

        // Allocate the destination geometry
        Geometry* pGeometry = NULL;
        if (APP_ERROR(AllocateGeometry(numVertices, numGeometryIndices, &pGeometry))("Couldn't allocate geometry"))
        {
            // Erase any geometry we made
            DeallocateGeometry(subsetGeometry);

            // Get rid of the mesh
            pSubsetIB->Unlock();
            pSubsetIB->Release();
            d3dxMesh->UnlockVertexBuffer();
            d3dxMesh->Release();

            // Free our device
            pd3dDevice->Release();

            // Error!
            return false;
        }

        // Copy the vertices needed for this subset into the buffer
        GeometryVertex* pVertices = pGeometry->pVertices;
        for (XIndicesIterator i = xIndicesTable.begin(); i != xIndicesTable.end(); ++i)
        {
            GeometryVertex* pCurrentVertex = &pVertices[i->second];
            *pCurrentVertex = pXVertices[i->first];

            // Modify the vertex location to make this a unit mesh sitting on the X-Z plane
            pCurrentVertex->x = pCurrentVertex->x;
            pCurrentVertex->y = pCurrentVertex->y;
            pCurrentVertex->z = pCurrentVertex->z;

            //pVertices[i->second].color = D3DCOLOR_XRGB(255,255,255);
            // todo: enable color?
        }

        // Copy triangles into the indices buffer
        DWORD index = 0;
        GeometryIndex* pIndices = pGeometry->pIndices;
        DWORD windingOrder = 0;
        for (TriangleIterator t = triangles.begin(); t != triangles.end(); ++t)
        {
            // Find this index in the winding list
            if (windingChanges.find(index / 3) != windingChanges.end())
                windingOrder = 1 - windingOrder;

            // Alternate the winding order so that everything shows up correctly
            if ((index / 3) % 2 == windingOrder)
            {
                pIndices[index + 0] = t->index[0];
                pIndices[index + 1] = t->index[1];
                pIndices[index + 2] = t->index[2];
            }
            else
            {
                pIndices[index + 0] = t->index[1];
                pIndices[index + 1] = t->index[0];
                pIndices[index + 2] = t->index[2];
            }

            // Increment the index counter
            index += 3;
        }

        // Unlock and delete strip index buffer
        pSubsetIB->Unlock();
        pSubsetIB->Release();

        // Store the buffers in the main array
        std::pair<SubsetGeometry::iterator,bool> result =
            subsetGeometry->insert(SubsetGeometry::value_type(subset, pGeometry));

        if (APP_ERROR(!result.second)("Couldn't insert subset geometry into main array for .X mesh"))
        {
            // Get rid of this geometry
            DeallocateGeometry(pGeometry);
            DeallocateGeometry(subsetGeometry);

            // Erase the mesh
            d3dxMesh->UnlockVertexBuffer();
            d3dxMesh->Release();

            // Free our device
            pd3dDevice->Release();

            // Return error
            return false;
        }

        //DEBUG_MSG("Subset %i has %i vertices %i indices (%i polygons)\n", subset, numVertices, numGeometryIndices, numGeometryIndices / 3);
    }

    // Done with the DirectX mesh.  This will not erase the outside mesh.
    d3dxMesh->UnlockVertexBuffer();
    d3dxMesh->Release();

    // Free the device reference
    pd3dDevice->Release();

    // Success
    return true;
}
Beispiel #21
0
void Renderer::Render( void )
{
	for (unsigned i = 0; i < m_renderUnitList.size(); i++)
	{
		RenderUnit* renderUnit = m_renderUnitList[i];

		unsigned nVerts = renderUnit->m_vb->Size();
		unsigned nTriangles = renderUnit->m_ib->Size() / 3;

		VsOutList vsOuts(nVerts);
		TriangleList triangles;

		// 对每个顶点执行VS
		for (unsigned j = 0; j < nVerts; j++)
		{
			vsOuts[j] = renderUnit->m_vs->Main((*renderUnit->m_vb)[j]);
		}

		// triangle setup (trivial-rejection, clipping)
		for (unsigned j = 0; j < nTriangles; j++)
		{
			Triangle tri;
			tri.iV0 = (*renderUnit->m_ib)[3 * j + 0];
			tri.iV1 = (*renderUnit->m_ib)[3 * j + 1];
			tri.iV2 = (*renderUnit->m_ib)[3 * j + 2];

			if (TrivialReject(tri, vsOuts))
			{
				continue;
			}
			else if (RemoveBackface(tri, vsOuts, CULL_MODE_CCW))
			{
				continue;
			}
			else if (TrivialAccept(tri, vsOuts))
			{
				triangles.push_back(tri);
			}
			else
			{
				// TODO: do clipping here
				triangles.push_back(tri);
			}
		}

		int halfRtWidth = m_renderTarget->GetWidth() >> 1;
		int halfRtHeight = m_renderTarget->GetHeight() >> 1;

		for (unsigned j = 0; j < vsOuts.size(); j++)
		{
			Vector4& position = vsOuts[j].position;

			// perspective-divide
			position.x /= position.w; 
			position.y /= position.w;
			position.z /= position.w;

			// 转化到屏幕坐标
			position.x = ( position.x + 1.0f) * halfRtWidth;
			position.y = (-position.y + 1.0f) * halfRtHeight;
		}
		
		// 光栅化每个三角形
		for (unsigned j = 0; j < triangles.size(); j++)
		{
			VertexShaderOutput& v0 = vsOuts[triangles[j].iV0];
			VertexShaderOutput& v1 = vsOuts[triangles[j].iV1];
			VertexShaderOutput& v2 = vsOuts[triangles[j].iV2];

			if (renderUnit->m_wireFrame)	// fill mode: wireframe
			{
				int x0 = int(v0.position.x);
				int y0 = int(v0.position.y);
				int x1 = int(v1.position.x);
				int y1 = int(v1.position.y);
				int x2 = int(v2.position.x);
				int y2 = int(v2.position.y);

				DrawLine(x0, y0, x1, y1, 0xFFFFFFFF);
				DrawLine(x0, y0, x2, y2, 0xFFFFFFFF);
				DrawLine(x1, y1, x2, y2, 0xFFFFFFFF);
			}
			else	// fill mode: solid
			{
				VertexShaderOutput* sv;
				VertexShaderOutput* mv;
				VertexShaderOutput* ev;
				
				// 按Y值给三个顶点排序
				if (v0.position.y < v1.position.y)
				{
					sv = &v0;
					ev = &v1;
				}
				else
				{
					sv = &v1;
					ev = &v0;
				}

				if (v2.position.y < sv->position.y)
				{
					mv = sv;
					sv = &v2;
				}
				else if (v2.position.y > ev->position.y)
				{
					mv = ev;
					ev = &v2;
				}
				else
				{
					mv = &v2;
				}

				// 扫描线算法
				// --begin--

				// 这种fill convention会导致由两个三角形共享的边上的像素被重绘一次,而顶点所在的像素会重绘更多次。
				// 但是算法实现简单,而且保证没有漏洞(漏洞比重绘更致命)
				int minY = int(sv->position.y);
				int maxY = int(ev->position.y);
				int midY = int(mv->position.y);

				float esY = ev->position.y - sv->position.y;
				float msY = mv->position.y - sv->position.y;
				float emY = ev->position.y - mv->position.y;

				float subPixelYOffsetS = sv->position.y - int(sv->position.y);
				float subPixelYOffsetM = mv->position.y - int(mv->position.y);

				float dx1 = (ev->position.x - sv->position.x) / esY;
				float dx2 = (mv->position.x - sv->position.x) / msY;	
				float dx2Alt = (ev->position.x - mv->position.x) / emY;

				float x1 = sv->position.x;
				float x2 = sv->position.x;

				for (int y = minY; y < maxY; y++)
				{
					if (y == midY)
					{
						dx2 = dx2Alt;
						x2 = mv->position.x;
					}

					float fY = y < midY ? y + subPixelYOffsetS : y + subPixelYOffsetM;

					VertexShaderOutput& va1 = Lerp(*sv, *ev, (ev->position.y - fY) / esY);

					VertexShaderOutput& va2 = y < midY
						? Lerp(*sv, *mv, (mv->position.y - fY) / msY)
						: Lerp(*mv, *ev, (ev->position.y - fY) / emY);

					if (x1 < x2)
					{
						FillSpan(x1, x2, y, va1, va2, *renderUnit->m_ps);
					}
					else
					{
						FillSpan(x2, x1, y, va2, va1, *renderUnit->m_ps);
					}

					x1 += dx1;
					x2 += dx2;
				}

				// 扫描线算法
				// --end--
			}
		}

		delete renderUnit;
	}

	m_renderUnitList.clear();
}
void ProgressiveTriangleGeometry::FindEdgeToCollapse(VertexList& /*org_vertex_list*/,
	TriangleList& /*org_triangle_list*/, VertexList& vertex_list, TriangleList& triangle_list, Edge& edge) {
	if (triangle_list.empty())
		return;

	float current_error = 0.0f;
	float current_max_error = 0.0f;

	edge.v1_ = 0;
	edge.v2_ = 0;
	edge.triangle_list_.clear();

	// Calculate mean error.
	VertexList::iterator v_iter;
	for (v_iter = vertex_list.begin();
	     v_iter != vertex_list.end();
	     ++v_iter) {
		if (v_iter == vertex_list.begin()) {
			current_max_error = (*v_iter)->error_;
		} else if((*v_iter)->error_ > current_max_error) {
			current_max_error = (*v_iter)->error_;
		}

		current_error += (*v_iter)->error_;
	}

	current_error /= (float)vertex_list.size();

	float min_error = 0.0f;
	float min_error1 = 0.0f;	// Temporary error value storage for _edge->v1_.
	float min_error2 = 0.0f;	// Temporary error value storage for _edge->v2_.
	bool first = true;

	// Test vertex collaps on all triangles.
	TriangleList::iterator tri_iter;
	for (tri_iter = triangle_list.begin();
	     tri_iter != triangle_list.end();
	     ++tri_iter) {
		Triangle* triangle = *tri_iter;
		vec3 diff1;
		vec3 diff2;
		Vertex mid;

		// Test V1 and V2.
		mid.x() = (triangle->v1_->x() + triangle->v2_->x()) * 0.5f;
		mid.y() = (triangle->v1_->y() + triangle->v2_->y()) * 0.5f;
		mid.z() = (triangle->v1_->z() + triangle->v2_->z()) * 0.5f;

		// Calculate the distance between the new, merged position,
		// and the original vertex position.
		diff1.Set(mid.x() - triangle->v1_->twin_->x(),
		            mid.y() - triangle->v1_->twin_->y(),
		            mid.z() - triangle->v1_->twin_->z());
		diff2.Set(mid.x() - triangle->v2_->twin_->x(),
		            mid.y() - triangle->v2_->twin_->y(),
		            mid.z() - triangle->v2_->twin_->z());

		float error1 = diff1.GetLength() + triangle->v1_->error_;
		float error2 = diff2.GetLength() + triangle->v2_->error_;
		float error = (error1 + error2 + current_error) / 3.0f;

		if (first == true || error < min_error) {
			edge.v1_ = triangle->v1_;
			edge.v2_ = triangle->v2_;
			min_error1 = error1;
			min_error2 = error2;
			min_error = error;
			first = false;
		}

		// Test V2 and V3.
		mid.x() = (triangle->v2_->x() + triangle->v3_->x()) * 0.5f;
		mid.y() = (triangle->v2_->y() + triangle->v3_->y()) * 0.5f;
		mid.z() = (triangle->v2_->z() + triangle->v3_->z()) * 0.5f;

		// Calculate the distance between the new, merged position,
		// and the original vertex position.
		diff1.Set(mid.x() - triangle->v2_->twin_->x(),
		            mid.y() - triangle->v2_->twin_->y(),
		            mid.z() - triangle->v2_->twin_->z());
		diff2.Set(mid.x() - triangle->v3_->twin_->x(),
		            mid.y() - triangle->v3_->twin_->y(),
		            mid.z() - triangle->v3_->twin_->z());

		error1 = diff1.GetLength() + triangle->v1_->error_;
		error2 = diff2.GetLength() + triangle->v2_->error_;
		error = (error1 + error2 + current_error) / 3.0f;

		if (error < min_error) {
			edge.v1_ = triangle->v1_;
			edge.v2_ = triangle->v2_;
			min_error = error;
			min_error1 = error1;
			min_error2 = error2;
		}

		// Test V3 and V1.
		mid.x() = (triangle->v3_->x() + triangle->v1_->x()) * 0.5f;
		mid.y() = (triangle->v3_->y() + triangle->v1_->y()) * 0.5f;
		mid.z() = (triangle->v3_->z() + triangle->v1_->z()) * 0.5f;

		// Calculate the distance between the new, merged position,
		// and the original vertex position.
		diff1.Set(mid.x() - triangle->v3_->twin_->x(),
		            mid.y() - triangle->v3_->twin_->y(),
		            mid.z() - triangle->v3_->twin_->z());
		diff2.Set(mid.x() - triangle->v1_->twin_->x(),
		            mid.y() - triangle->v1_->twin_->y(),
		            mid.z() - triangle->v1_->twin_->z());

		error1 = diff1.GetLength() + triangle->v1_->error_;
		error2 = diff2.GetLength() + triangle->v2_->error_;
		error = (error1 + error2 + current_error) / 3.0f;

		if (error < min_error) {
			edge.v1_ = triangle->v1_;
			edge.v2_ = triangle->v2_;
			min_error = error;
			min_error1 = error1;
			min_error2 = error2;
		}

		if (min_error == 0.0f && edge.v1_ != 0 && edge.v2_ != 0)
			break;
	}

	edge.v1_->error_ = min_error1;
	edge.v2_->error_ = min_error2;

	// Now add all triangles to _edge that share the two vertices
	// _edge->v1_ and _edge->v2_.
	for (tri_iter = triangle_list.begin();
		tri_iter != triangle_list.end();
		++tri_iter) {
		Triangle* triangle = *tri_iter;
		if (triangle->HaveVertex(edge.v1_) &&
			triangle->HaveVertex(edge.v2_)) {
			edge.triangle_list_.push_back(triangle);
		}
	}
}
unsigned int TriangleAdjacencyGraph::calcOptPrim ( unsigned extIteration,
    bool doStrip, bool doFan, 
    unsigned minFanTriangles )
{
  int iteration = extIteration;
  bool sample = iteration > 1 ? true : false;
  bool checkRevOrder = sample;
  TriangleList degreeBag[4];
  TriangleList *fList = 0;
  int cost = 0, sampleCost = 0;
  int stripCost = 0, revCost = 0, fanCost = 0, triCost = 0;
  int bestCost = 0, worstCost = 0, lowDegree;
  unsigned int i, n;
  WalkCase walkCase = START;
  Triangle *triangle, *next;
  HalfEdge *twin = 0, *gateEdge = 0, *halfEdge = 0;
  bool doMainLoop = true;
  unsigned int seed = 1, bestSeed = 1;
  int mostDegree = 3;
  unsigned triangleLeft = _trianglePool.countElem();
  srand(1);

  if (doFan) {
    n = _temporaryVector.size();
    fanCost = 0;

    // find fans 

    for (i = 0; i < n; i++) 
      if ( (_temporaryVector[i].size() >= minFanTriangles) &&
	  (gateEdge = _temporaryVector[i][0].second) &&
	  (gateEdge->triangle->valid()) ) {
	for ( halfEdge = gateEdge->next->next->twin;
	    (halfEdge && halfEdge->triangle->valid() && (halfEdge != gateEdge));
	    halfEdge = halfEdge->next->next->twin )
	  ;
	if (halfEdge == gateEdge) {
	  // fan is closed; mark every triangle          

	  triangle = 0;
	  fList = new TriangleList;
	  for ( halfEdge = gateEdge;
	      !triangle || (halfEdge != gateEdge);
	      halfEdge = halfEdge->next->next->twin ) {
	    triangle = halfEdge->triangle;
	    _validTriangleBag.release(*triangle);
	    triangle->drop();
	    triangle->state = FAN_PART;
	    fList->add(*triangle);
	  }
	  _fanBag.push_back(Primitive(i,fList));
	  fanCost += (_temporaryVector[i].size() + 2);
	  triangleLeft -= _temporaryVector[i].size();
	}
      }
  }

  if (doStrip && iteration) {

    // push every triangle into the according degree bag

    degreeBag[mostDegree].paste(_validTriangleBag);
    for (triangle = degreeBag[mostDegree].first; triangle; triangle = next) {
      next = triangle->next;
      if (triangle->valid()) {
	if (triangle->state != mostDegree) {
	  degreeBag[mostDegree].release(*triangle);
	  _validTriangleBag.release(*triangle);
	  degreeBag[triangle->state].add( *triangle);
	}
      }
      else {
	cerr << "INVALID TRIANGLE IN VALID TRIANGLE BAG\n" << endl;
      }
    }

    for (iteration--; iteration >= 0; iteration--) {

      seed = iteration ? rand() : bestSeed;
      srand (seed);

      fList = 0;
      cost = 0;
      doMainLoop = true;
      walkCase = START;

      // run the main loop

      while (doMainLoop) {

	switch (walkCase) {
	  case START:      

	    stripCost = 0;
	    triangle = 0;

	    for (lowDegree = 1; lowDegree < 4; lowDegree++)
	      if ((degreeBag[lowDegree].empty() == false)) {
		if (sample) {
		  // pick a random triangle

		  n = degreeBag[lowDegree].countElem() - 1;
		  i = int(float(n) * rand()/float(RAND_MAX));
		  triangle = degreeBag[lowDegree].first;
                  while (i--) 
                     triangle = triangle->next;
		}
		else {
		  // pick the first triangle

		  triangle = degreeBag[lowDegree].first;
		}              
		break;
	      }

	    if (triangle) {

	      // create the new list

	      fList = new TriangleList;

	      // find the best neighbour

	      gateEdge = 0;
	      for (i = 0; i < 3; i++) 
		if ( (twin = triangle->halfEdgeVec[i].twin) && 
		    (twin->triangle->state > 0) ) {
		  if ( twin->next->next->twin &&
		      (twin->next->next->twin->triangle->state > 0) ) {
		    gateEdge = &triangle->halfEdgeVec[i];
		    break;
		  }
		  else {
		    if ( twin->next->twin &&
			(twin->next->twin->triangle->state > 0) )
		      gateEdge = &triangle->halfEdgeVec[i];
		    else {
		      if ((twin->triangle->state > 0))
			gateEdge = &triangle->halfEdgeVec[i];
		    }
		  }
		}

	      // release and store the first triangle

	      dropOutTriangle (*triangle,degreeBag);
	      fList->add(*triangle);
	      stripCost += 3;

	      // set the next step

	      if (gateEdge) {          
		walkCase = LEFT;
		stripCost++;
	      }
	      else 
		walkCase = FINISH;
	    }
	    else
	      doMainLoop = false;      
	    break;

	  case LEFT:
	    gateEdge = gateEdge->twin;
	    triangle = gateEdge->triangle;

	    // find the next gate

	    if (triangle->state == DEGREE_0) {
	      gateEdge = 0;
	      walkCase = FINISH;
	    }
	    else
	      if ( (twin = gateEdge->next->next->twin) && 
		  (twin->triangle->state > 0) ){
		gateEdge = gateEdge->next->next;
		stripCost++;
		walkCase = RIGHT;
	      }
	      else {
		gateEdge = gateEdge->next;
		stripCost += 2;
		walkCase = LEFT;
	      }

	    // store the current triangle

	    dropOutTriangle (*triangle,degreeBag);
	    fList->add(*triangle);
	    break;

	  case RIGHT:      
	    gateEdge = gateEdge->twin;
	    triangle = gateEdge->triangle;

	    // find the next gate

	    if (triangle->state == DEGREE_0) {
	      gateEdge = 0;
	      walkCase = FINISH;
	    }
	    else
	      if ( (twin = gateEdge->next->twin) && 
		  (twin->triangle->state > 0) ) {
		gateEdge = gateEdge->next;
		stripCost++;
		walkCase = LEFT;
	      }
	      else {
		gateEdge = gateEdge->next->next;
		stripCost += 2;
		walkCase = RIGHT;
	      }

	    // store the current triangle

	    dropOutTriangle (*triangle,degreeBag);
	    fList->add(*triangle);
	    break;

	  case FINISH:      
	    // try to reverse the strip

	    if ( checkRevOrder &&
		(revCost = calcStripCost(*fList,true)) &&
		(revCost < stripCost) ) {
	      _stripBag.push_back(Primitive(1,fList));
	      cost += revCost;
	    }
	    else {
	      _stripBag.push_back(Primitive(0,fList));
	      cost += stripCost;
	    }
	    walkCase = START;
	    fList = 0;
	    break;
	}  
      }

      if (sample) {
	sampleCost = cost + (degreeBag[0].countElem() * 3) + fanCost;
	if (!bestCost || (sampleCost < bestCost)) {
	  bestCost = sampleCost;
	  bestSeed = seed;
	}
	if (sampleCost > worstCost)
	  worstCost = sampleCost;

	cout << " cost/best/worst: " 
	  << sampleCost << '/' << bestCost << '/' << worstCost
	  << endl;
      }

      if (iteration) {
	// reinit the four degree bags

	degreeBag[mostDegree].paste(degreeBag[0]);
	n = _stripBag.size();
	for (i = 0; i < n; i++) {
	  degreeBag[mostDegree].paste(*_stripBag[i].second);
	  delete _stripBag[i].second;
	}
	_stripBag.clear();
	for ( triangle = degreeBag[mostDegree].first; triangle; 
	    triangle = next) {
	  next = triangle->next;
	  triangle->resetDegreeState(STRIP_PART);
	  if (triangle->valid()) {
	    if (triangle->state != mostDegree) {
	      degreeBag[mostDegree].release(*triangle);
	      degreeBag[triangle->state].add(*triangle);
	    }
	  }
	  else {
	    cerr << "INVALID TRIANGLE IN REINIT\n" << endl;
	    cerr << triangle->state << endl;
	  }
	}
      }
    }
  }
  else {    
    // push every valid triangle in degree 0; we don't strip anything

    degreeBag[0].paste(_validTriangleBag);
  }

  if (sample) {
    cerr << "range: " 
      << bestCost << '/' << worstCost << ' '
      << float(100 * (worstCost-bestCost))/float(bestCost) << '%'
      << endl;
  }

  // collect isolated triangles  

  degreeBag[0].paste(_invalidTriangleBag);  
  triCost = degreeBag[0].countElem() * 3;
  if (triCost) {
    fList = new TriangleList;  
    fList->paste(degreeBag[0]);
    _triBag.push_back(Primitive(0,fList));
  }

  return (cost + fanCost + triCost);
}