예제 #1
0
파일: Mesh.hpp 프로젝트: pmoulon/DO-CV
 bool readObjFile(SimpleMesh<Vector, Face3>& mesh,
                  const std::string& fileName)
 {
   // Attempt to read file.
   std::ifstream file(fileName.c_str());
   if(!file)
   {
     std::cerr << "Error reading file!" << std::endl;
     return false;
   }
   // Clear the mesh data structure.
   mesh = SimpleMesh<Vector, Face3>();
   // Fill the mesh data structure.
   std::string line;
   while ( std::getline(file, line) )
   {
     std::stringstream ss;
     ss << line;
     char type;
     ss >> type;
     if(type=='v')
     {
       double x, y, z;
       ss >> x >> y >> z;
       mesh.vertices().push_back(Vector(x, y, z));
     }
     if(type=='f')
     {
       size_t a, b, c;
       ss >> a >> b >> c;
       mesh.faces().push_back(Face3(a-1, b-1, c-1));
     }
예제 #2
0
 inline void append_face(uint32_t a, uint32_t b, uint32_t c){ append_face(Face3(a, b, c)); }
예제 #3
0
파일: geometry.cpp 프로젝트: Alex-doc/godot
static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z, PoolVector<Face3> &p_faces) {

	ERR_FAIL_INDEX(x, len_x);
	ERR_FAIL_INDEX(y, len_y);
	ERR_FAIL_INDEX(z, len_z);

	if (p_cell_status[x][y][z] & _CELL_EXTERIOR)
		return;

/*	static const Vector3 vertices[8]={
		Vector3(0,0,0),
		Vector3(0,0,1),
		Vector3(0,1,0),
		Vector3(0,1,1),
		Vector3(1,0,0),
		Vector3(1,0,1),
		Vector3(1,1,0),
		Vector3(1,1,1),
	};
*/
#define vert(m_idx) Vector3((m_idx & 4) >> 2, (m_idx & 2) >> 1, m_idx & 1)

	static const uint8_t indices[6][4] = {
		{ 7, 6, 4, 5 },
		{ 7, 3, 2, 6 },
		{ 7, 5, 1, 3 },
		{ 0, 2, 3, 1 },
		{ 0, 1, 5, 4 },
		{ 0, 4, 6, 2 },

	};
	/*

		{0,1,2,3},
		{0,1,4,5},
		{0,2,4,6},
		{4,5,6,7},
		{2,3,7,6},
		{1,3,5,7},

		{0,2,3,1},
		{0,1,5,4},
		{0,4,6,2},
		{7,6,4,5},
		{7,3,2,6},
		{7,5,1,3},
*/

	for (int i = 0; i < 6; i++) {

		Vector3 face_points[4];
		int disp_x = x + ((i % 3) == 0 ? ((i < 3) ? 1 : -1) : 0);
		int disp_y = y + (((i - 1) % 3) == 0 ? ((i < 3) ? 1 : -1) : 0);
		int disp_z = z + (((i - 2) % 3) == 0 ? ((i < 3) ? 1 : -1) : 0);

		bool plot = false;

		if (disp_x < 0 || disp_x >= len_x)
			plot = true;
		if (disp_y < 0 || disp_y >= len_y)
			plot = true;
		if (disp_z < 0 || disp_z >= len_z)
			plot = true;

		if (!plot && (p_cell_status[disp_x][disp_y][disp_z] & _CELL_EXTERIOR))
			plot = true;

		if (!plot)
			continue;

		for (int j = 0; j < 4; j++)
			face_points[j] = vert(indices[i][j]) + Vector3(x, y, z);

		p_faces.push_back(
				Face3(
						face_points[0],
						face_points[1],
						face_points[2]));

		p_faces.push_back(
				Face3(
						face_points[2],
						face_points[3],
						face_points[0]));
	}
}
예제 #4
0
파일: mesh.cpp 프로젝트: luchete80/GodotGUI
Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texel_size) {

	ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
	ERR_EXPLAIN("Can't unwrap mesh with blend shapes");
	ERR_FAIL_COND_V(blend_shapes.size() != 0, ERR_UNAVAILABLE);

	Vector<float> vertices;
	Vector<float> normals;
	Vector<int> indices;
	Vector<int> face_materials;
	Vector<float> uv;
	Vector<Pair<int, int> > uv_index;

	Vector<ArrayMeshLightmapSurface> surfaces;
	for (int i = 0; i < get_surface_count(); i++) {
		ArrayMeshLightmapSurface s;
		s.primitive = surface_get_primitive_type(i);

		if (s.primitive != Mesh::PRIMITIVE_TRIANGLES) {
			ERR_EXPLAIN("Only triangles are supported for lightmap unwrap");
			ERR_FAIL_V(ERR_UNAVAILABLE);
		}
		s.format = surface_get_format(i);
		if (!(s.format & ARRAY_FORMAT_NORMAL)) {
			ERR_EXPLAIN("Normals are required for lightmap unwrap");
			ERR_FAIL_V(ERR_UNAVAILABLE);
		}

		Array arrays = surface_get_arrays(i);
		s.material = surface_get_material(i);
		s.vertices = SurfaceTool::create_vertex_array_from_triangle_arrays(arrays);

		PoolVector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX];
		int vc = rvertices.size();
		PoolVector<Vector3>::Read r = rvertices.read();

		PoolVector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL];
		PoolVector<Vector3>::Read rn = rnormals.read();

		int vertex_ofs = vertices.size() / 3;

		vertices.resize((vertex_ofs + vc) * 3);
		normals.resize((vertex_ofs + vc) * 3);
		uv_index.resize(vertex_ofs + vc);

		for (int j = 0; j < vc; j++) {

			Vector3 v = p_base_transform.xform(r[j]);
			Vector3 n = p_base_transform.basis.xform(rn[j]).normalized();

			vertices[(j + vertex_ofs) * 3 + 0] = v.x;
			vertices[(j + vertex_ofs) * 3 + 1] = v.y;
			vertices[(j + vertex_ofs) * 3 + 2] = v.z;
			normals[(j + vertex_ofs) * 3 + 0] = n.x;
			normals[(j + vertex_ofs) * 3 + 1] = n.y;
			normals[(j + vertex_ofs) * 3 + 2] = n.z;
			uv_index[j + vertex_ofs] = Pair<int, int>(i, j);
		}

		PoolVector<int> rindices = arrays[Mesh::ARRAY_INDEX];
		int ic = rindices.size();

		if (ic == 0) {

			for (int j = 0; j < vc / 3; j++) {
				if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate())
					continue;

				indices.push_back(vertex_ofs + j * 3 + 0);
				indices.push_back(vertex_ofs + j * 3 + 1);
				indices.push_back(vertex_ofs + j * 3 + 2);
				face_materials.push_back(i);
			}

		} else {
			PoolVector<int>::Read ri = rindices.read();

			for (int j = 0; j < ic / 3; j++) {
				if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate())
					continue;
				indices.push_back(vertex_ofs + ri[j * 3 + 0]);
				indices.push_back(vertex_ofs + ri[j * 3 + 1]);
				indices.push_back(vertex_ofs + ri[j * 3 + 2]);
				face_materials.push_back(i);
			}
		}

		surfaces.push_back(s);
	}

	//unwrap

	float *gen_uvs;
	int *gen_vertices;
	int *gen_indices;
	int gen_vertex_count;
	int gen_index_count;
	int size_x;
	int size_y;

	bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), face_materials.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y);

	if (!ok) {
		return ERR_CANT_CREATE;
	}

	//remove surfaces
	while (get_surface_count()) {
		surface_remove(0);
	}

	//create surfacetools for each surface..
	Vector<Ref<SurfaceTool> > surfaces_tools;

	for (int i = 0; i < surfaces.size(); i++) {
		Ref<SurfaceTool> st;
		st.instance();
		st->begin(Mesh::PRIMITIVE_TRIANGLES);
		st->set_material(surfaces[i].material);
		surfaces_tools.push_back(st); //stay there
	}

	print_line("gen indices: " + itos(gen_index_count));
	//go through all indices
	for (int i = 0; i < gen_index_count; i += 3) {

		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_index.size(), ERR_BUG);
		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_index.size(), ERR_BUG);
		ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_index.size(), ERR_BUG);

		ERR_FAIL_COND_V(uv_index[gen_vertices[gen_indices[i + 0]]].first != uv_index[gen_vertices[gen_indices[i + 1]]].first || uv_index[gen_vertices[gen_indices[i + 0]]].first != uv_index[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);

		int surface = uv_index[gen_vertices[gen_indices[i + 0]]].first;

		for (int j = 0; j < 3; j++) {

			SurfaceTool::Vertex v = surfaces[surface].vertices[uv_index[gen_vertices[gen_indices[i + j]]].second];

			if (surfaces[surface].format & ARRAY_FORMAT_COLOR) {
				surfaces_tools[surface]->add_color(v.color);
			}
			if (surfaces[surface].format & ARRAY_FORMAT_TEX_UV) {
				surfaces_tools[surface]->add_uv(v.uv);
			}
			if (surfaces[surface].format & ARRAY_FORMAT_NORMAL) {
				surfaces_tools[surface]->add_normal(v.normal);
			}
			if (surfaces[surface].format & ARRAY_FORMAT_TANGENT) {
				Plane t;
				t.normal = v.tangent;
				t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
				surfaces_tools[surface]->add_tangent(t);
			}
			if (surfaces[surface].format & ARRAY_FORMAT_BONES) {
				surfaces_tools[surface]->add_bones(v.bones);
			}
			if (surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) {
				surfaces_tools[surface]->add_weights(v.weights);
			}

			Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
			surfaces_tools[surface]->add_uv2(uv2);

			surfaces_tools[surface]->add_vertex(v.vertex);
		}
	}

	//free stuff
	::free(gen_vertices);
	::free(gen_indices);
	::free(gen_uvs);

	//generate surfaces

	for (int i = 0; i < surfaces_tools.size(); i++) {
		surfaces_tools[i]->index();
		surfaces_tools[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), surfaces[i].format);
	}

	set_lightmap_size_hint(Size2(size_x, size_y));

	return OK;
}
예제 #5
0
void TriangleMesh::create(const DVector<Vector3>& p_faces) {

	valid=false;

	int fc=p_faces.size();
	ERR_FAIL_COND(!fc || ((fc%3) != 0));
	fc/=3;
	triangles.resize(fc);

	bvh.resize(fc*3); //will never be larger than this (todo make better)
	DVector<BVH>::Write bw = bvh.write();

	{

		//create faces and indices and base bvh
		//except for the Set for repeated triangles, everything
		//goes in-place.

		DVector<Vector3>::Read r = p_faces.read();
		DVector<Triangle>::Write w = triangles.write();
		Map<Vector3,int> db;

		for(int i=0;i<fc;i++) {

			Triangle&f=w[i];
			const Vector3 *v=&r[i*3];

			for(int j=0;j<3;j++) {

				int vidx=-1;
				Vector3 vs=v[j].snapped(0.0001);
				Map<Vector3,int>::Element *E=db.find(vs);
				if (E) {
					vidx=E->get();
				} else {
					vidx=db.size();
					db[vs]=vidx;
				}

				f.indices[j]=vidx;
				if (j==0)
					bw[i].aabb.pos=vs;
				else
					bw[i].aabb.expand_to(vs);
			}

			f.normal=Face3(r[i*3+0],r[i*3+1],r[i*3+2]).get_plane().get_normal();

			bw[i].left=-1;
			bw[i].right=-1;
			bw[i].face_index=i;
			bw[i].center=bw[i].aabb.pos+bw[i].aabb.size*0.5;
		}

		vertices.resize(db.size());
		DVector<Vector3>::Write vw = vertices.write();
		for (Map<Vector3,int>::Element *E=db.front();E;E=E->next()) {
			vw[E->get()]=E->key();
		}

	}

	DVector<BVH*> bwptrs;
	bwptrs.resize(fc);
	DVector<BVH*>::Write bwp = bwptrs.write();
	for(int i=0;i<fc;i++) {

		bwp[i]=&bw[i];
	}

	max_depth=0;
	int max_alloc=fc;
	int max=_create_bvh(bw.ptr(),bwp.ptr(),0,fc,1,max_depth,max_alloc);

	bw=DVector<BVH>::Write(); //clearup
	bvh.resize(max_alloc); //resize back

	valid=true;

}
예제 #6
0
파일: face3.cpp 프로젝트: Calinou/godot
int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_over[3]) const {

	ERR_FAIL_COND_V(is_degenerate(), 0);

	Vector3 above[4];
	int above_count = 0;

	Vector3 below[4];
	int below_count = 0;

	for (int i = 0; i < 3; i++) {

		if (p_plane.has_point(vertex[i], CMP_EPSILON)) { // point is in plane

			ERR_FAIL_COND_V(above_count >= 4, 0);
			above[above_count++] = vertex[i];
			ERR_FAIL_COND_V(below_count >= 4, 0);
			below[below_count++] = vertex[i];

		} else {

			if (p_plane.is_point_over(vertex[i])) {
				//Point is over
				ERR_FAIL_COND_V(above_count >= 4, 0);
				above[above_count++] = vertex[i];

			} else {
				//Point is under
				ERR_FAIL_COND_V(below_count >= 4, 0);
				below[below_count++] = vertex[i];
			}

			/* Check for Intersection between this and the next vertex*/

			Vector3 inters;
			if (!p_plane.intersects_segment(vertex[i], vertex[(i + 1) % 3], &inters))
				continue;

			/* Intersection goes to both */
			ERR_FAIL_COND_V(above_count >= 4, 0);
			above[above_count++] = inters;
			ERR_FAIL_COND_V(below_count >= 4, 0);
			below[below_count++] = inters;
		}
	}

	int polygons_created = 0;

	ERR_FAIL_COND_V(above_count >= 4 && below_count >= 4, 0); //bug in the algo

	if (above_count >= 3) {

		p_res[polygons_created] = Face3(above[0], above[1], above[2]);
		p_is_point_over[polygons_created] = true;
		polygons_created++;

		if (above_count == 4) {

			p_res[polygons_created] = Face3(above[2], above[3], above[0]);
			p_is_point_over[polygons_created] = true;
			polygons_created++;
		}
	}

	if (below_count >= 3) {

		p_res[polygons_created] = Face3(below[0], below[1], below[2]);
		p_is_point_over[polygons_created] = false;
		polygons_created++;

		if (below_count == 4) {

			p_res[polygons_created] = Face3(below[2], below[3], below[0]);
			p_is_point_over[polygons_created] = false;
			polygons_created++;
		}
	}

	return polygons_created;
}
예제 #7
0
파일: Face3.cpp 프로젝트: caomw/Three.cpp
 Face3 Face3::clone() const {
     return Face3(*this);
 }
예제 #8
0
void BakedLightBaker::_octree_insert(const AABB& p_aabb,Octant *p_octant,Triangle* p_triangle, int p_depth) {

	if (p_octant->leaf) {

		if (p_aabb.has_point(p_triangle->vertices[0]) && p_aabb.has_point(p_triangle->vertices[1]) &&p_aabb.has_point(p_triangle->vertices[2])) {
			//face is completely enclosed, add area
			p_octant->surface_area+=Face3(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).get_area();
		} else {
			//not completely enclosed, will need to be clipped..
			Vector<Vector3> poly;
			poly.push_back(p_triangle->vertices[0]);
			poly.push_back(p_triangle->vertices[1]);
			poly.push_back(p_triangle->vertices[2]);

			//clip
			for(int i=0;i<3;i++) {

				//top plane
				Plane p(0,0,0,0);
				p.normal[i]=1.0;
				p.d=p_aabb.pos[i]+p_aabb.size[i];
				poly=Geometry::clip_polygon(poly,p);

				//bottom plane
				p.normal[i]=-1.0;
				p.d=-p_aabb.pos[i];
				poly=Geometry::clip_polygon(poly,p);
			}
			//calculate area
			for(int i=2;i<poly.size();i++) {
				p_octant->surface_area+=Face3(poly[0],poly[i-1],poly[i]).get_area();
			}
		}

	} else {


		for(int i=0;i<8;i++) {

			AABB aabb=p_aabb;
			aabb.size*=0.5;
			if (i&1)
				aabb.pos.x+=aabb.size.x;
			if (i&2)
				aabb.pos.y+=aabb.size.y;
			if (i&4)
				aabb.pos.z+=aabb.size.z;

			AABB fit_aabb=aabb;
			//fit_aabb=fit_aabb.grow(bvh->aabb.size.x*0.0001);

			if (!Face3(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).intersects_aabb(fit_aabb))
				continue;

			if (!p_octant->children[i]) {
				p_octant->children[i]=memnew(Octant);
				if (p_depth==0) {
					p_octant->children[i]->leaf=true;
					p_octant->children[i]->light_accum[0]=0;
					p_octant->children[i]->light_accum[1]=0;
					p_octant->children[i]->light_accum[2]=0;
					p_octant->children[i]->offset[0]=aabb.pos.x+aabb.size.x*0.5;
					p_octant->children[i]->offset[1]=aabb.pos.y+aabb.size.y*0.5;
					p_octant->children[i]->offset[2]=aabb.pos.z+aabb.size.z*0.5;
					p_octant->children[i]->surface_area=0;
					p_octant->children[i]->next_leaf=leaf_list;
					leaf_list=p_octant->children[i];
					cell_count++;
				} else {

					p_octant->children[i]->leaf=false;
					for(int j=0;j<8;j++) {
						p_octant->children[i]->children[j]=0;
					}
				}
			}

			_octree_insert(aabb,p_octant->children[i],p_triangle,p_depth-1);
		}
	}
}
예제 #9
0
  void initialize( float radius,
                   float segmentsWidth,
                   float segmentsHeight,
                   float phiStart,
                   float phiLength,
                   float thetaStart,
                   float thetaLength ) {

    const auto segmentsX = Math::max( 3, ( int )Math::floor( segmentsWidth ) );
    const auto segmentsY = Math::max( 2, ( int )Math::floor( segmentsHeight ) );

    std::vector<std::vector<int>> indices;
    std::vector<std::vector<UV>> uvs;

    for ( int y = 0; y <= segmentsY; y ++ ) {

      std::vector<int> indicesRow;
      std::vector<UV> uvsRow;

      for ( int x = 0; x <= segmentsX; x ++ ) {

        const auto u = ( float )x / segmentsX;
        const auto v = ( float )y / segmentsY;

        Vertex vertex;
        vertex.x = - radius * Math::cos( phiStart + u * phiLength ) * Math::sin( thetaStart + v * thetaLength );
        vertex.y = radius * Math::cos( thetaStart + v * thetaLength );
        vertex.z = radius * Math::sin( phiStart + u * phiLength ) * Math::sin( thetaStart + v * thetaLength );

        vertices.push_back( vertex );

        indicesRow.push_back( ( int )vertices.size() - 1 );
        uvsRow.push_back( UV( u, 1 - v ) );

      }

      indices.push_back( indicesRow );
      uvs.push_back( uvsRow );

    }

    for ( int y = 0; y < segmentsY; y ++ ) {

      for ( int x = 0; x < segmentsX; x ++ ) {

        const auto v1 = indices[ y ][ x + 1 ];
        const auto v2 = indices[ y ][ x ];
        const auto v3 = indices[ y + 1 ][ x ];
        const auto v4 = indices[ y + 1 ][ x + 1 ];

        const auto n1 = vertices[ v1 ].clone().normalize();
        const auto n2 = vertices[ v2 ].clone().normalize();
        const auto n3 = vertices[ v3 ].clone().normalize();
        const auto n4 = vertices[ v4 ].clone().normalize();

        const auto& uv1 = uvs[ y ][ x + 1 ];
        const auto& uv2 = uvs[ y ][ x ];
        const auto& uv3 = uvs[ y + 1 ][ x ];
        const auto& uv4 = uvs[ y + 1 ][ x + 1 ];

        if ( Math::abs( vertices[ v1 ].y ) == radius ) {

          faces.push_back( Face3( v1, v3, v4, n1, n3, n4 ) );
          faceVertexUvs[ 0 ].push_back( toArray( uv1, uv3, uv4 ) );

        } else if ( Math::abs( vertices[ v3 ].y ) ==  radius ) {

          faces.push_back( Face3( v1, v2, v3, n1, n2, n3 ) );
          faceVertexUvs[ 0 ].push_back( toArray( uv1, uv2, uv3 ) );

        } else {

          faces.push_back( Face4( v1, v2, v3, v4, n1, n2, n3, n4 ) );
          faceVertexUvs[ 0 ].push_back( toArray( uv1, uv2, uv3, uv4 ) );

        }

      }

    }

    computeCentroids();
    computeFaceNormals();

    boundingSphere.radius = radius;

  }
예제 #10
0
void TorusKnotGeometry::initialize( float radius,
                 float tube,
                 size_t radialSegments,
                 size_t tubularSegments,
                 float p,
                 float q,
                 float heightScale ) {

	std::vector<std::vector<int>> grid;
  grid.resize( radialSegments );

	auto tang = Vector3();
	auto n = Vector3();
	auto bitan = Vector3();

	for ( size_t i = 0; i < radialSegments; ++ i ) {


		auto u = (float)i / (float)radialSegments * 2.f * p * Math::PI();
		Vector3 p1 = getPos( u, q, p, radius, heightScale );
		Vector3 p2 = getPos( u + 0.01f, q, p, radius, heightScale );

		tang.subVectors( p2, p1 );
		n.addVectors( p2, p1 );

		bitan.crossVectors( tang, n );
		n.crossVectors( bitan, tang );
		bitan.normalize();
		n.normalize();

		for ( size_t j = 0; j < tubularSegments; ++ j ) {

			auto v = (float)j / (float)tubularSegments * 2.f * Math::PI();
			auto cx = - tube * Math::cos( v ); // TODO: Hack: Negating it so it faces outside.
			auto cy = tube * Math::sin( v );

			auto pos = Vector3();
			pos.x = p1.x + cx * n.x + cy * bitan.x;
			pos.y = p1.y + cx * n.y + cy * bitan.y;
			pos.z = p1.z + cx * n.z + cy * bitan.z;

      this->vertices.push_back( pos );
			grid[ i ].push_back( this->vertices.size() - 1 );

		}

	}

	for ( size_t i = 0; i < radialSegments; ++ i ) {

		for ( size_t j = 0; j < tubularSegments; ++ j ) {

			auto ip = ( i + 1 ) % radialSegments;
			auto jp = ( j + 1 ) % tubularSegments;

			auto a = grid[ i ][ j ];
			auto b = grid[ ip ][ j ];
			auto c = grid[ ip ][ jp ];
			auto d = grid[ i ][ jp ];

			auto uva = Vector2( (float)i / (float)radialSegments, (float)j / (float)tubularSegments );
			auto uvb = Vector2( (float)( i + 1 ) / (float)radialSegments, (float)j / (float)tubularSegments );
			auto uvc = Vector2( (float)( i + 1 ) / (float)radialSegments, (float)( j + 1 ) / (float)tubularSegments );
			auto uvd = Vector2( (float)i / (float)radialSegments, (float)( j + 1 ) / (float)tubularSegments );

			this->faces.push_back( Face3( a, b, d ) );
			this->faceVertexUvs[ 0 ].push_back( toArray( uva, uvb, uvd ) );

			this->faces.push_back( Face3( b, c, d ) );
			this->faceVertexUvs[ 0 ].push_back( toArray( uvb.clone(), uvc, uvd.clone() ) );

		}
	}

	this->computeCentroids();
	this->computeFaceNormals();
	this->computeVertexNormals();

}