Exemple #1
0
int ClosestContact(const ContactPoint& p,const Meshing::TriMesh& mesh,ContactPoint& pclose,Real normalScale)
{
  int closest = -1;
  Real closestDist2 = Inf;
  Triangle3D tri;
  Plane3D plane;
  for(size_t i=0;i<mesh.tris.size();i++) {
    mesh.GetTriangle(i,tri);
    //first check distance to supporting plane, since it's a lower bound
    tri.getPlane(plane);
    Real dxmin = plane.distance(p.x);
    Real dn = normalScale*plane.normal.distanceSquared(p.n);
    if(dn + Sqr(dxmin) < closestDist2) {
      //has potential to be closer than previous
      Vector3 cp = tri.closestPoint(p.x);
      Real d = cp.distanceSquared(p.x) + dn;
      if(d < closestDist2) {
	closest = (int)i;
	closestDist2 = d;
	pclose.x = cp;
	pclose.n = plane.normal;
	pclose.kFriction = p.kFriction;
      }
    }
  }
  return closest;
}
//Returns a contact normal for the closest point to the triangle t.  p is the point on the triangle.
//The direction is the one in which triangle 1 can move to get away from closestpt
Vector3 ContactNormal(const CollisionMesh& m,const Vector3& p,int t,const Vector3& closestPt)
{
  Triangle3D tri;
  m.GetTriangle(t,tri);
  Vector3 b=tri.barycentricCoords(p);
  int type=FeatureType(b);
  switch(type) {
  case 1:  //pt
    //get the triangle normal
    {
      Vector3 n = VertexNormal(m,t,VertexIndex(b));
      n.inplaceNegative();
      return n;
    }
    break;
  case 2:  //edge
    {
      int e = EdgeIndex(b);
      Vector3 n = EdgeNormal(m,t,e);
      n.inplaceNegative();
      return n;
    }
    break;
  case 3:  //face
    return m.currentTransform.R*(-tri.normal());
  }
  static int warnedCount = 0;
  if(warnedCount % 10000 == 0) 
    printf("ODECustomMesh: Warning, degenerate triangle, types %d\n",type);
  warnedCount++;
  //AssertNotReached();
  return Vector3(Zero);
}
Exemple #3
0
int ClosestPointDescent(const TriMeshWithTopology& m,const Vector3& p,int tri,Vector3& cp)
{
  Assert(m.tris.size() == m.triNeighbors.size());
  Assert(tri >= 0 && tri < (int)m.tris.size());
  Triangle3D t;
  Vector3 temp;
  Real closestd=Inf;
  FastFindHeap<int,Real> q;  //sorted from highest to lowest
  set<int> visited;
  q.push(tri,-Inf);
  while(!q.empty()) {
    int curtri = q.top(); q.pop();
    visited.insert(curtri);

    m.GetTriangle(curtri,t);
    temp = t.closestPoint(p);
    Real d=temp.distance(p);
    if(d < closestd) {
      tri = curtri;
      closestd = d;
      cp = temp;
      //traverse to neighbors
      for(int i=0;i<3;i++) {
	int neighbor = m.triNeighbors[curtri][i];
	if(neighbor >= 0) {
	  if(visited.find(neighbor)==visited.end())
	    q.adjust(neighbor,-d);
	}
      }
    }
  } 
  return tri;
}
Exemple #4
0
Real Polygon3D::areaConvex() const
{
  Real sum=0;
  Triangle3D temp;
  for(size_t i=1;i+1<vertices.size();i++) {
    temp.set(vertices[0],vertices[i],vertices[i+1]);
    sum += temp.area();
  }
  return sum;
}
Exemple #5
0
bool Box3D::intersects(const Triangle3D& t) const
{
  Triangle3D tloc;
  toLocal(t.a,tloc.a);
  toLocal(t.b,tloc.b);
  toLocal(t.c,tloc.c);
  AABB3D bbloc;
  bbloc.bmin.setZero();
  bbloc.bmax=dims;
  return tloc.intersects(bbloc);
}
int MeshPrimitiveCollide(CollisionMesh& m1,Real outerMargin1,GeometricPrimitive3D& g2,const RigidTransform& T2,Real outerMargin2,dContactGeom* contact,int maxcontacts)
{
  GeometricPrimitive3D gworld=g2;
  gworld.Transform(T2);
  Sphere3D s;
  if(gworld.type != GeometricPrimitive3D::Point && gworld.type != GeometricPrimitive3D::Sphere) {
    fprintf(stderr,"Distance computations between Triangles and %s not supported\n",gworld.TypeName());
    return 0;
  }
  if(gworld.type == GeometricPrimitive3D::Point) {
    s.center = *AnyCast<Point3D>(&gworld.data);
    s.radius = 0;
  }
  else {
    s = *AnyCast<Sphere3D>(&gworld.data);
  }
    
  Real tol = outerMargin1 + outerMargin2;
  Triangle3D tri;
  vector<int> tris;
  int k=0;
  NearbyTriangles(m1,gworld,tol,tris,maxcontacts);
  for(size_t j=0;j<tris.size();j++) {   
    m1.GetTriangle(tris[j],tri);
    tri.a = m1.currentTransform*tri.a;
    tri.b = m1.currentTransform*tri.b;
    tri.c = m1.currentTransform*tri.c;

    Vector3 cp = tri.closestPoint(s.center);
    Vector3 n = cp - s.center;
    Real nlen = n.length();
    Real d = nlen-s.radius;
    Vector3 pw = s.center;
    if(s.radius > 0)
      //adjust pw to the sphere surface
      pw += n*(s.radius/nlen);
    if(d < gNormalFromGeometryTolerance) {  //compute normal from the geometry
      Vector3 plocal;
      m1.currentTransform.mulInverse(cp,plocal);
      n = ContactNormal(m1,plocal,tris[j],pw);
    }
    else if(d > tol) {  //some penetration -- we can't trust the result of PQP
      continue;
    }
    else n /= nlen;
    //migrate the contact point to the center of the overlap region
    CopyVector(contact[k].pos,0.5*(cp+pw) + ((outerMargin2 - outerMargin1)*0.5)*n);
    CopyVector(contact[k].normal,n);
    contact[k].depth = tol - d;
    k++;
    if(k == maxcontacts) break;
  }
  return k;
}
bool ColDetect::coldetect(Triangle3D tri1, Triangle3D tri2, double *trans1, double *trans2) {//wrapper, so I would not have to deal with arrays
	int ntri1 = 1;	//number of rows in first array (one row for one Triangle, propably)
	int ntri2 = 1;	//number of rows in second array (one row for one Triangle, propably)
	int ntrans1 = 1;	//number of rows for transformation matrix
	int ntrans2 = 1;	//number of rows for transformation matrix
	std::vector<double> tri1vector = tri1.toVector();
	std::vector<double> tri2vector = tri2.toVector();
	double tri1array[9];
	double tri2array[9];
	copy(tri1vector.begin(), tri1vector.end(), tri1array);
	copy(tri2vector.begin(), tri2vector.end(), tri2array);
	return coldetect(ntri1, ntri2, ntrans1, ntrans2, tri1array, tri2array, trans1, trans2);
}
Exemple #8
0
Vector3 Polygon3D::centroidConvex() const
{
  const static Real Third = 1.0/3.0;
  Vector3 c(Zero);
  Real sum=0;
  Triangle3D temp;
  for(size_t i=1;i+1<vertices.size();i++) {
    temp.set(vertices[0],vertices[i],vertices[i+1]);
    Real area = temp.area();
    c.madd((temp.a+temp.b+temp.c),area*Third);
    sum += area;
  }
  if(sum == 0) return c;
  return c/sum;
}
bool Triangle3D::intersects(const Triangle3D& T, Segment3D& S) const
{
  //intersect this plane with t
  Plane3D p;
  getPlane(p);
  if(!T.intersects(p,S)) return false;

  //now limit S to the boundaries of this triangle
  //find segment in plane coordinates (uA,vA)->(uB,vB)
  //clip against constraints u,v >= 0, u+v<=1
  Vector2 A,D;
  A = planeCoords(S.A);
  D = planeCoords(S.B)-A;
  Real tmin=Zero,tmax=One;
  //(u,v) = A+t*D
  //1) u >= 0 => -uA+t*(-uD) <= 0
  if(!ClipLine1D(-A.x,-D.x,tmin,tmax)) return false;
  //2) v >= 0 => -vA+t*(-vD) <= 0
  if(!ClipLine1D(-A.y,-D.y,tmin,tmax)) return false;
  //3) u+v <= 1 => (uA+vA-1)+t*(uD+vD) <= 0
  if(!ClipLine1D(A.x+A.y-One, D.x+D.y,tmin,tmax)) return false;
  Vector2 U=A; U.madd(D,tmin);
  S.A = planeCoordsToPoint(U);
  U=A; U.madd(D,tmax);
  S.B = planeCoordsToPoint(U);
  return true;
}
Exemple #10
0
int TriMesh::ClosestPoint(const Vector3& pt,Vector3& cp) const
{
  int tmin=-1;
  Real dmin=Inf;
  Vector3 temp;
  Triangle3D tri;
  for(size_t i=0;i<tris.size();i++) {
    GetTriangle(i,tri);
    temp = tri.closestPoint(pt);
    Real d2=temp.distanceSquared(pt);
    if(d2 < dmin) {
      tmin = (int)i;
      dmin = d2;
      cp = temp;
    }
  }
  return tmin;
}
Exemple #11
0
int TriMesh::RayCast(const Ray3D& r,Vector3& pt) const
{
  int tmin=-1;
  Real dmin=Inf;
  Real d;
  Vector2 uv;
  Triangle3D tri;
  for(size_t i=0;i<tris.size();i++) {
    GetTriangle(i,tri);
    if(tri.rayIntersects(r,&d,&uv.x,&uv.y)) {
      if(d < dmin) {
	tmin = (int)i;
	dmin = d;
	pt = tri.planeCoordsToPoint(uv);
      }
    }
  }
  return tmin;
}
Exemple #12
0
// Determines if 3D segment and triangle have a unique intersection.  
// If true and rpoint is not NULL, returns intersection point.
bool Intersects(const Segment3D& seg, const Triangle3D& tri, Point3D *rpoint)
{
  Vector3D n = ( tri[1] - tri[0] ) ^ ( tri[2] - tri[1] );

  float a = n[0], b = n[1], c = n[2];
  float d = -( a * tri[0][0] + b * tri[0][1] + c * tri[0][2] );

  if ( !Intersects( seg, Plane3D( a, b, c, d ), rpoint ) )
    return false;

  return tri.contains( *rpoint );
}
Exemple #13
0
void Triangle3D::edgeIntersections(const Triangle3D& T, Real u[3]) const
{
    Plane3D PT;
    T.getPlane(PT);
    Real da,db,dc;
    da=PT.distance(a);
    db=PT.distance(b);
    dc=PT.distance(c);

    u[0]=u[1]=u[2]=-One;

    //check to see if these points are within T's boundaries
    Vector3 x;
    Vector2 U;
    Real ui;
    //edge a,b
    ui = SegmentZeroCrossing(da,db);
    if(ui >= Zero && ui <= One) {
        interpolate(a,b,ui,x);
        U = T.planeCoords(x);
        if(containsPlaneCoords(U)) u[0] = ui;
    }

    //edge b,c
    ui = SegmentZeroCrossing(db,dc);
    if(ui >= Zero && ui <= One) {
        interpolate(b,c,ui,x);
        U = T.planeCoords(x);
        if(containsPlaneCoords(U)) u[1] = ui;
    }

    //edge c,a
    ui = SegmentZeroCrossing(dc,da);
    if(ui >= Zero && ui <= One) {
        interpolate(c,a,ui,x);
        U = T.planeCoords(x);
        if(containsPlaneCoords(U)) u[2] = ui;
    }
}
Exemple #14
0
//data loading
bool ModelData::LoadIn(QString filepath)
{	
    bool loaderReady;
    bool abort;

    STLTri* pLoadedTri = NULL;
    Triangle3D newtri;

	this->filepath = filepath;
	
	if(filepath.isEmpty())
		return false;
	
	//extract filename from path!
    filename = QFileInfo(filepath).fileName();

    B9ModelLoader mLoader(filepath,loaderReady,NULL);

    if(loaderReady == false)//error opening model data
	{
        //display Loader Error
		QMessageBox msgBox;
        msgBox.setText(mLoader.GetError());
        msgBox.exec();
		return false;
	}

    //make a progress bar and connect it to
    LoadingBar loadbar(0,100);
    loadbar.useCancelButton(false);
    loadbar.setDescription("Importing: " + filename);
    QObject::connect(&mLoader,SIGNAL(PercentCompletedUpdate(qint64,qint64)),
                     &loadbar,SLOT(setProgress(qint64,qint64)));



    //now we are ready to walk the loader through reading each triangle
    //and copying it into the this model data.
    while(mLoader.LoadNextTri(pLoadedTri,abort))
    {
        if(abort)
        {
            //display Loader abort error
            QMessageBox msgBox;
            msgBox.setText(mLoader.GetError());
            msgBox.exec();
            return false;
        }
        else
        {
            //newtri.normal.setX(pLoadedTri->nx);
            //newtri.normal.setY(pLoadedTri->ny);
            //newtri.normal.setZ(pLoadedTri->nz);

            newtri.vertex[0].setX(pLoadedTri->x0);
            newtri.vertex[0].setY(pLoadedTri->y0);
            newtri.vertex[0].setZ(pLoadedTri->z0);
            newtri.vertex[1].setX(pLoadedTri->x1);
            newtri.vertex[1].setY(pLoadedTri->y1);
            newtri.vertex[1].setZ(pLoadedTri->z1);
            newtri.vertex[2].setX(pLoadedTri->x2);
            newtri.vertex[2].setY(pLoadedTri->y2);
            newtri.vertex[2].setZ(pLoadedTri->z2);

            //use right hand rule for importing normals - ignore file normals..
            newtri.UpdateNormalFromGeom();

            delete pLoadedTri;
            newtri.UpdateBounds();

            triList.push_back(newtri);
        }
    }



    qDebug() << "Loaded triangles: " << triList.size();
	//now center it!
	CenterModel();

    //generate a normal display lists.

    int displaySuccess = FormNormalDisplayLists();

    if(displaySuccess)
        return true;
    else
        return false;
}
Exemple #15
0
int Slice::GenerateSegments(B9ModelInstance* inputInstance)
{
    unsigned int t;
	int v1;
	int v2;
    int cmpcount = 0;//0 or 1 来指示你所寻找的点;

    QVector3D* thisvert = NULL;//局部指针进行快速访问顶点
	QVector3D* thatvert = NULL;

    std::vector<Triangle3D*>* rangedTriangles;

	double xdisp;
	double ydisp;
	double zdisp;
	double planefraction;

	int intersections = 0;


	//Triangle Splitting here:
    //Get the right container of triangles and use that as the list
    //(we used to use the triList which was O(n^2))
    rangedTriangles = inputInstance->GetTrianglesAroundZ(realAltitude);


    for(t = 0; t < rangedTriangles->size(); t++)//for each triangle in the model
	{
		//we want to create a temporary pointer to the currenct triangle
        Triangle3D* pTransTri = rangedTriangles->at(t);

		//test if the triangle intersects the XY plane of this slice!
		if(!pTransTri->IntersectsXYPlane(realAltitude))
		{
            continue;
		}
			
		intersections++;
		cmpcount = 0;
		
		//create 1 new segment object for the end result 
		Segment* seg1 = new Segment;
		QVector2D points[2];
		for(v1=0;v1<3;v1++)//for 1 or 2 triangle verts ABOVE the plane:
		{
			thisvert = &pTransTri->vertex[v1];
			if(thisvert->z() <= realAltitude)//we only want to compare FROM above the plane by convention (yes this means flush triangles below the plane)
			{
				continue;
			}
			for(v2=0; v2<3; v2++)//to every other triangle vert
			{
				if(v2 == v1)
				{continue;}

				thatvert = &pTransTri->vertex[v2];

				//are both points on the same side of plane?
				//if so we dont want to compare
				if((thatvert->z() > realAltitude))
				{
					continue;
				}
					
				cmpcount++;
				//common
				//displacments (final - initial)
				xdisp = thatvert->x() - thisvert->x();
				ydisp = thatvert->y() - thisvert->y();
				zdisp = thatvert->z() - thisvert->z();

                planefraction = (thisvert->z() - realAltitude)/fabs(zdisp);//0-1 fraction of where the plane is in relation to the z distance between the 2 verts.
				//(0 would be the plane is at the hieght of thisvert)

				points[cmpcount-1].setX(thisvert->x() + xdisp*planefraction);
				points[cmpcount-1].setY(thisvert->y() + ydisp*planefraction);
			}
		}
	
		//initiallize the segment.
		seg1->normal.setX(pTransTri->normal.x());
		seg1->normal.setY(pTransTri->normal.y());

		seg1->p1.setX(points[0].x());
		seg1->p1.setY(points[0].y());

		seg1->p2.setX(points[1].x());
		seg1->p2.setY(points[1].y());

		seg1->normal.normalize();

		seg1->CorrectPointOrder();//to match the normal convention!
			
		AddSegment(seg1);
	}
	return segmentList.size();
}
Exemple #16
0
//data loading
bool ModelData::LoadIn(QString filepath)
{	
    unsigned int m;
    unsigned int t;
    unsigned int i;

    Triangle3D newtri;
    const struct aiFace* face;

	
	this->filepath = filepath;
	
	if(filepath.isEmpty())
		return false;
	
	//extract filename from path!
	filename = QFileInfo(filepath).baseName();

	//AI_CONFIG_PP_FD_REMOVE = aiPrimitiveType_POINTS | aiPrimitiveType_LINES;
    pScene = aiImportFile(filepath.toAscii(), aiProcess_Triangulate);// | aiProcess_JoinIdenticalVertices); //trian
	
    if(pScene == NULL)//assimp cant handle the file - lets try our own reader.
	{
		//display Assimp Error
		QMessageBox msgBox;
		msgBox.setText("Assimp Error:  " + QString().fromAscii(aiGetErrorString()));
		msgBox.exec();

        aiReleaseImport(pScene);

		return false;
	}

    qDebug() << "Model imported with " << pScene->mMeshes[0]->mNumFaces << " faces.";
	

	for (m = 0; m < pScene->mNumMeshes; m++) 
	{
		const aiMesh* mesh = pScene->mMeshes[m];
		
	    for (t = 0; t < mesh->mNumFaces; t++)
		{
            face = &mesh->mFaces[t];
			
			if(face->mNumIndices == 3)
			{
				for(i = 0; i < face->mNumIndices; i++) 
				{
					int index = face->mIndices[i];
				
                    newtri.normal.setX(mesh->mNormals[index].x);
                    newtri.normal.setY(mesh->mNormals[index].y);
                    newtri.normal.setZ(mesh->mNormals[index].z);
			
                    newtri.vertex[i].setX(mesh->mVertices[index].x);
                    newtri.vertex[i].setY(mesh->mVertices[index].y);
                    newtri.vertex[i].setZ(mesh->mVertices[index].z);
				}
                newtri.UpdateBounds();
                triList.push_back(newtri);

			}
		}
	}

    aiReleaseImport(pScene);

    qDebug() << "Loaded triangles: " << triList.size();
	//now center it!
	CenterModel();

	//generate a displaylist
    int displayerror = FormDisplayList();

    //check for errors in display list creation (if its a large model the card may run out of memory.

    if(displayerror){
    while(displayerror)//loop and see if there are additional errors as well.
    {
        //display Assimp Error
        qDebug() << "Display List Error: " << displayerror; //write to log as well.
        QMessageBox msgBox;

        switch(displayerror)
        {
        case GL_OUT_OF_MEMORY:
            msgBox.setText("OpenGL Error:  GL_OUT_OF_MEMORY\nModel is too large to render on your graphics card.");
            break;
        case GL_INVALID_ENUM:
            msgBox.setText("OpenGL Error:  GL_INVALID_ENUM");
            break;
        case GL_INVALID_VALUE:
            msgBox.setText("OpenGL Error:  GL_INVALID_VALUE");
            break;
        case GL_INVALID_FRAMEBUFFER_OPERATION:
            msgBox.setText("OpenGL Error:  GL_INVALID_FRAMEBUFFER_OPERATION");
            break;
        case GL_STACK_UNDERFLOW:
            msgBox.setText("OpenGL Error:  GL_STACK_UNDERFLOW");
            break;
        case GL_STACK_OVERFLOW:
            msgBox.setText("OpenGL Error:  GL_STACK_OVERFLOW");
            break;
        default:
            break;
        }

        msgBox.exec();
        displayerror = glGetError();
    }
    return false;
    }




	return true;
}
//Baking
//using all rotations scales positions etc, generates the list of triangles that
//is represented by the rendered shapes.
//similar to instance baking.
//remember that this function needed to always reflect what render() does but inverted.
void B9SupportStructure::BakeToInstanceGeometry()
{
    unsigned int t;
    unsigned short int v;
    Triangle3D* pNewTri;
    QVector3D topScale;
    QVector3D topPos;
    QVector3D midScale;
    QVector3D midPos;
    QVector3D bottomScale;
    QVector3D bottomPos;


    //first bake the top
    topScale = QVector3D(topRadius*2.0,topRadius*2.0,topLength + topPenetration);
    topPos = QVector3D((topPenetrationPoint.x() + topPivot.x())*0.5
                          ,(topPenetrationPoint.y() + topPivot.y())*0.5
                          ,(topPenetrationPoint.z() + topPivot.z())*0.5);
    if(topAttachShape != NULL)
    for(t = 0; t < topAttachShape->GetTriangles()->size(); t++)
    {
        pNewTri = new Triangle3D(topAttachShape->GetTriangles()->at(t));


        for(v=0;v<3;v++)
        {
            //scale triangle vertices 1st
            pNewTri->vertex[v] *= topScale;

            //Rotate 2nd
            //dont deal with y - not needed in rendering or baking
            RotateVector(pNewTri->vertex[v], topThetaX, QVector3D(1,0,0));//x
            RotateVector(pNewTri->vertex[v], topThetaZ, QVector3D(0,0,1));//z

            //Translate 3rd into global space
            pNewTri->vertex[v] += topPos;
            pNewTri->vertex[v] += instanceParent->GetPos();
        }

        //Update the triangle bounds and normal
        pNewTri->UpdateBounds();
        pNewTri->UpdateNormalFromGeom();

        //Add To List
        instanceParent->triList.push_back(pNewTri);
    }

    //second bake the middle
    midScale = QVector3D(midRadius*2.0,midRadius*2.0,midLength);
    midPos = QVector3D((topMidExtensionPoint.x() + bottomMidExtensionPoint.x())*0.5
                      ,(topMidExtensionPoint.y() + bottomMidExtensionPoint.y())*0.5
                      ,(topMidExtensionPoint.z() + bottomMidExtensionPoint.z())*0.5);

    if(midAttachShape != NULL)
    for(t = 0; t < midAttachShape->GetTriangles()->size(); t++)
    {
        pNewTri = new Triangle3D(midAttachShape->GetTriangles()->at(t));



        for(v=0;v<3;v++)
        {
            //scale triangle vertices 1st
            pNewTri->vertex[v] *= midScale;

            //Rotate 2nd
            //dont deal with y - not needed in rendering or baking
            RotateVector(pNewTri->vertex[v], midThetaX, QVector3D(1,0,0));//x
            RotateVector(pNewTri->vertex[v], midThetaZ, QVector3D(0,0,1));//z

            //Translate 3rd into global space
            pNewTri->vertex[v] += midPos;
            pNewTri->vertex[v] += instanceParent->GetPos();
        }

        //Update the triangle bounds and normal
        pNewTri->UpdateBounds();
        pNewTri->UpdateNormalFromGeom();

        //Add To List
        instanceParent->triList.push_back(pNewTri);
    }

    //third bake the bottom
    bottomScale = QVector3D(bottomRadius*2.0,bottomRadius*2.0,bottomLength + bottomPenetration);
    bottomPos = QVector3D((bottomPenetrationPoint.x() + bottomPivot.x())*0.5
                          ,(bottomPenetrationPoint.y() + bottomPivot.y())*0.5
                          ,(bottomPenetrationPoint.z() + bottomPivot.z())*0.5);

    if(bottomAttachShape != NULL)
    for(t = 0; t < bottomAttachShape->GetTriangles()->size(); t++)
    {
        pNewTri = new Triangle3D(bottomAttachShape->GetTriangles()->at(t));



        for(v=0;v<3;v++)
        {
            //scale triangle vertices 1st
            pNewTri->vertex[v] *= bottomScale;

            //Rotate 2nd
            //dont deal with y - not needed in rendering or baking
            RotateVector(pNewTri->vertex[v], bottomThetaX+180, QVector3D(1,0,0));//x
            RotateVector(pNewTri->vertex[v], bottomThetaZ, QVector3D(0,0,1));//z

            //Translate 3rd into global space
            pNewTri->vertex[v] += bottomPos;
            pNewTri->vertex[v] += instanceParent->GetPos();
        }

        //Update the triangle bounds and normal
        pNewTri->UpdateBounds();
        pNewTri->UpdateNormalFromGeom();

        //Add To List
        instanceParent->triList.push_back(pNewTri);
    }

}
//Imports
void B9SupportStructure::ImportAttachmentDataFromStls()
{
    int i;
    bool s;
    bool triangleError;
    STLTri* pLoadedTri = NULL;
    Triangle3D newtri;
    QDir appdir(CROSS_OS_GetDirectoryFromLocationTag("APPLICATION_DIR"));
    QStringList filters;
    B9SupportAttachmentData importedData;

    qDebug() << "B9SupportStructure: Importing Stl Attachments...";

    //Import All Stl in the application directory begining with "SUPPORT"
    filters << "SUPPORT_*";
    appdir.setNameFilters(filters);
    QFileInfoList stlFiles = appdir.entryInfoList();
    for(i = 0; i < stlFiles.size(); i++)
    {
        B9ModelLoader stlLoader(stlFiles[i].filePath(),s);
        if(s)
        {
            while(stlLoader.LoadNextTri(pLoadedTri,triangleError))
            {
                if(triangleError)
                {
                    qDebug() << "B9SupportStructure: ErrorLoading triangle";
                    return;
                }
                newtri.normal.setX(pLoadedTri->nx);
                newtri.normal.setY(pLoadedTri->ny);
                newtri.normal.setZ(pLoadedTri->nz);

                newtri.vertex[0].setX(pLoadedTri->x0);
                newtri.vertex[0].setY(pLoadedTri->y0);
                newtri.vertex[0].setZ(pLoadedTri->z0);
                newtri.vertex[1].setX(pLoadedTri->x1);
                newtri.vertex[1].setY(pLoadedTri->y1);
                newtri.vertex[1].setZ(pLoadedTri->z1);
                newtri.vertex[2].setX(pLoadedTri->x2);
                newtri.vertex[2].setY(pLoadedTri->y2);
                newtri.vertex[2].setZ(pLoadedTri->z2);

                delete pLoadedTri;
                newtri.UpdateBounds();

                importedData.GetTriangles()->push_back(newtri);
            }

            //save the data into the Static List.
            importedData.SetName(stlFiles[i].baseName().remove("SUPPORT_"));
            importedData.CenterGeometry();//center the triangles around 0,0,0
            B9SupportStructure::AttachmentDataList.push_back(importedData);
            //clear out importedData
            importedData = B9SupportAttachmentData();
        }
        else
        {
            qDebug() << "B9SupportStructure: Error Loading: " << stlFiles[i].fileName();
        }

    }

    qDebug() << "B9SupportStructure: Succesfully Loaded "
             << B9SupportStructure::AttachmentDataList.size()
             << " Attachment Types";
}