Example #1
0
mesh import_mesh(char const * file_name) {
	Assimp::Importer importer;

	aiScene const * ai_scene = importer.ReadFile(
		file_name,
		aiProcess_Triangulate |
		aiProcess_JoinIdenticalVertices |
		aiProcess_GenNormals |
		aiProcess_SortByPType
	);

	if (!ai_scene) throw std::runtime_error(std::string("Unable to import mesh: ") + importer.GetErrorString());

	aiMesh const & am = *ai_scene->mMeshes[0]; // TODO: What about multiple meshes?

	vertices vertices;

	{
		buffer<hvector4<float>> positions(am.mNumVertices);
		buffer<vector3<float>> normals(am.mNumVertices);
		for(unsigned int i = 0; i < am.mNumVertices; ++i) {
			positions[i] = { am.mVertices[i][0], am.mVertices[i][1], am.mVertices[i][2] };
			normals  [i] = { am. mNormals[i][0], am. mNormals[i][1], am. mNormals[i][2] };
		}
		vertices.attribute("position", std::move(positions));
		vertices.attribute("normal"  , std::move(normals  ));
	}

	for (size_t n = 0; n < am.GetNumColorChannels(); ++n) {
		buffer<hvector4<float>> colors(am.mNumVertices);
		for(unsigned int i = 0; i < am.mNumVertices; ++i) {
			auto const & c = am.mColors[n][i];
			colors[i] = {c.r, c.g, c.b, c.a};
		}
		std::ostringstream name("color", std::ios_base::ate);
		if (n) name << (n + 1);
		vertices.attribute(name.str(), std::move(colors));
	}

	for (size_t n = 0; n < am.GetNumUVChannels(); ++n) {
		buffer<vector3<float>> uvs(am.mNumVertices);
		for(unsigned int i = 0; i < am.mNumVertices; ++i) {
			uvs[i] = { am.mTextureCoords[n][i][0], am.mTextureCoords[n][i][1], am.mTextureCoords[n][i][1] };
		}
		std::ostringstream name("texture_coordinate", std::ios_base::ate);
		if (n) name << (n + 1);
		vertices.attribute(name.str(), std::move(uvs));
	}

	buffer<GLushort> indices(am.mNumFaces * 3);

	for (size_t i = 0; i < am.mNumFaces; ++i) {
		indices[i*3  ] = am.mFaces[i].mIndices[0];
		indices[i*3+1] = am.mFaces[i].mIndices[1];
		indices[i*3+2] = am.mFaces[i].mIndices[2];
	}

	return mesh(std::move(vertices), std::move(indices));
}
//-*****************************************************************************
// much like lib/Alembic/Abc/OTypedProperty.cpp, this is just a compile test,
// due to the implementation being in the .h file, due to templates.
void __testOGeomParamCompile( Abc::OCompoundProperty &iParent )
{
    OV2fGeomParam uvs( iParent, "uv", false, kVertexScope, 1 );

    std::vector<V2f> vec;

    vec.push_back( V2f( 1.0f, 2.0f ) );

    V2fArraySample val( vec );

    OV2fGeomParam::Sample samp( val, kUnknownScope );

    uvs.set( samp );
}
// =======================================================================
// Read all faces from TRIANGLES, TRIANGLE_FANS, POLYLIST, POLYGON
// Important: This function MUST be called before read_lines() 
// Otherwise we will loose all edges from faces (see read_lines() above)
//
// TODO: import uv set names
// ========================================================================
void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
{
	unsigned int i;
	
	allocate_poly_data(collada_mesh, me);

	UVDataWrapper uvs(collada_mesh->getUVCoords());
	VCOLDataWrapper vcol(collada_mesh->getColors());

	MPoly *mpoly = me->mpoly;
	MLoop *mloop = me->mloop;
	int loop_index = 0;

	MaterialIdPrimitiveArrayMap mat_prim_map;

	COLLADAFW::MeshPrimitiveArray& prim_arr = collada_mesh->getMeshPrimitives();
	COLLADAFW::MeshVertexData& nor = collada_mesh->getNormals();

	for (i = 0; i < prim_arr.getCount(); i++) {
		
		COLLADAFW::MeshPrimitive *mp = prim_arr[i];

		// faces
		size_t prim_totpoly                           = mp->getFaceCount();
		unsigned int *position_indices                = mp->getPositionIndices().getData();
		unsigned int *normal_indices                  = mp->getNormalIndices().getData();


		bool mp_has_normals = primitive_has_useable_normals(mp);
		bool mp_has_faces   = primitive_has_faces(mp);

		int collada_meshtype = mp->getPrimitiveType();
		
		// since we cannot set mpoly->mat_nr here, we store a portion of me->mpoly in Primitive
		Primitive prim = {mpoly, 0};

		// If MeshPrimitive is TRIANGLE_FANS we split it into triangles
		// The first trifan vertex will be the first vertex in every triangle
		// XXX The proper function of TRIANGLE_FANS is not tested!!!
		// XXX In particular the handling of the normal_indices looks very wrong to me
		if (collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLE_FANS) {
			unsigned grouped_vertex_count = mp->getGroupedVertexElementsCount();
			for (unsigned int group_index = 0; group_index < grouped_vertex_count; group_index++) {
				unsigned int first_vertex = position_indices[0]; // Store first trifan vertex
				unsigned int first_normal = normal_indices[0]; // Store first trifan vertex normal
				unsigned int vertex_count = mp->getGroupedVerticesVertexCount(group_index);

				for (unsigned int vertex_index = 0; vertex_index < vertex_count - 2; vertex_index++) {
					// For each triangle store indeces of its 3 vertices
					unsigned int triangle_vertex_indices[3] = {first_vertex, position_indices[1], position_indices[2]};
					set_poly_indices(mpoly, mloop, loop_index, triangle_vertex_indices, 3);

					if (mp_has_normals) {  // vertex normals, same inplementation as for the triangles
						// the same for vertces normals
						unsigned int vertex_normal_indices[3] = {first_normal, normal_indices[1], normal_indices[2]};
						if (!is_flat_face(vertex_normal_indices, nor, 3))
							mpoly->flag |= ME_SMOOTH;
						normal_indices++;
					}
				
					mpoly++;
					mloop += 3;
					loop_index += 3;
					prim.totpoly++;

				}

				// Moving cursor  to the next triangle fan.
				if (mp_has_normals)
					normal_indices += 2;

				position_indices +=  2;
			}
		}

		if (collada_meshtype == COLLADAFW::MeshPrimitive::POLYLIST ||
			collada_meshtype == COLLADAFW::MeshPrimitive::POLYGONS ||
			collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLES) {
			COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp;
			unsigned int start_index = 0;

			COLLADAFW::IndexListArray& index_list_array_uvcoord = mp->getUVCoordIndicesArray();
			COLLADAFW::IndexListArray& index_list_array_vcolor  = mp->getColorIndicesArray();

			for (unsigned int j = 0; j < prim_totpoly; j++) {
				
				// Vertices in polygon:
				int vcount = get_vertex_count(mpvc, j);
				set_poly_indices(mpoly, mloop, loop_index, position_indices, vcount);


				for (unsigned int uvset_index = 0; uvset_index < index_list_array_uvcoord.getCount(); uvset_index++) {
					// get mtface by face index and uv set index
					COLLADAFW::IndexList& index_list = *index_list_array_uvcoord[uvset_index];
					MLoopUV  *mloopuv = (MLoopUV  *)CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, index_list.getName().c_str());
					if (mloopuv == NULL) {
						fprintf(stderr, "Collada import: Mesh [%s] : Unknown reference to TEXCOORD [#%s].\n", me->id.name, index_list.getName().c_str() );
					}
					else {
						set_face_uv(mloopuv+loop_index, uvs, start_index, *index_list_array_uvcoord[uvset_index], vcount);
					}
				}

				if (mp_has_normals) {
					if (!is_flat_face(normal_indices, nor, vcount))
						mpoly->flag |= ME_SMOOTH;
				}


				if (mp->hasColorIndices()) {
					int vcolor_count = index_list_array_vcolor.getCount();

					for (unsigned int vcolor_index = 0; vcolor_index < vcolor_count; vcolor_index++) {

						COLLADAFW::IndexList& color_index_list = *mp->getColorIndices(vcolor_index);
						COLLADAFW::String colname = extract_vcolname(color_index_list.getName());
						MLoopCol *mloopcol = (MLoopCol  *)CustomData_get_layer_named(&me->ldata, CD_MLOOPCOL, colname.c_str());
						if (mloopcol == NULL) {
							fprintf(stderr, "Collada import: Mesh [%s] : Unknown reference to VCOLOR [#%s].\n", me->id.name, color_index_list.getName().c_str());
						}
						else {
							set_vcol(mloopcol + loop_index, vcol, start_index, color_index_list, vcount);
						}
					}
				}

				mpoly++;
				mloop       += vcount;
				loop_index  += vcount;
				start_index += vcount;
				prim.totpoly++;

				if (mp_has_normals)
					normal_indices += vcount;

				position_indices += vcount;
			}
		}

		else if (collada_meshtype == COLLADAFW::MeshPrimitive::LINES) {
			continue; // read the lines later after all the rest is done
		}

		if (mp_has_faces)
			mat_prim_map[mp->getMaterialId()].push_back(prim);


	}

	geom_uid_mat_mapping_map[collada_mesh->getUniqueId()] = mat_prim_map;
}
Example #4
0
	AssetLoader::submeshptr_type AssimpAssetLoader::buildSubMesh(const aiNode *node, int mMeshes_index, aiMatrix4x4 &worldSpace)
	{
		unsigned int mesh_index = node->mMeshes[mMeshes_index];
		aiMesh *ai_mesh = mScene->mMeshes[mesh_index];
		
		// create the submesh
		submeshptr_type submesh = createSubMeshImpl(node->mName.C_Str());

		// copy the verts to memory managed by us
		std::vector<Vertex> verts(ai_mesh->mNumVertices);
		AssimpHelpers::copyAiVector3DToGLM(verts.data(), ai_mesh->mNumVertices, ai_mesh->mVertices);

		// copy the face indices to memory managed by us
		Face faces[ai_mesh->mNumFaces];
		AssimpHelpers::copyAiFaceToGLM(faces, ai_mesh->mNumFaces, ai_mesh->mFaces);

		// create the material
		Material *mat = getMaterial(ai_mesh->mMaterialIndex);
		setSubMeshMaterialImpl(submesh, mat);

		setSubMeshGeometryImpl(submesh, verts.data(), ai_mesh->mNumVertices, faces, ai_mesh->mNumFaces);
		if(ai_mesh->GetNumUVChannels() > 0 && ai_mesh->mNumUVComponents[0] == 2) 
		{
			// we only support 2 component texture lookups on texture 0
			// copy the verts to memory managed by us
			std::vector<TexCoord> uvs(ai_mesh->mNumVertices);
			AssimpHelpers::copyAiTexCoordToGLM(uvs.data(), ai_mesh->mNumVertices, ai_mesh->mTextureCoords[0]);
			setSubMeshUVsImpl(submesh, uvs.data(), ai_mesh->mNumVertices);
		}

		// if we have normals, grab them too
		if(ai_mesh->HasNormals())
		{
			std::vector<Vertex> normals(ai_mesh->mNumVertices);
			AssimpHelpers::copyAiVector3DToGLM(normals.data(), ai_mesh->mNumVertices, ai_mesh->mNormals);
			setSubMeshNormalsImpl(submesh, normals.data(), ai_mesh->mNumVertices);
		}

/*		// process any skeleton
		if(ai_mesh->HasBones())
		{
			std::cout << "got a skeleton" << std::endl;
			for (int i = 0; i < ai_mesh->mNumBones; ++i)
			{
				aiBone *bone = ai_mesh->mBones[i];
				std::cout << "\tBone: " << bone->mName.C_Str() 
					<< " mNumWeights: " << bone->mNumWeights << std::endl;

				aiNode *boneNode = mScene->mRootNode->FindNode(bone->mName);
				if(boneNode)
				{
					std::cout << "\tboneNode" <<  boneNode->mName.C_Str() << std::endl; 
				}
				else
				{
					std::cout << "Error could not find Node in heirarchy for bone " 
						<< bone->mName.C_Str() << std::endl; 
				}
			}
		}*/

		return submesh;
	}
Example #5
0
std::vector<std::shared_ptr<Shape>> CreateNURBS(const Transform *o2w,
                                                const Transform *w2o,
                                                bool reverseOrientation,
                                                const ParamSet &params) {
    int nu = params.FindOneInt("nu", -1);
    if (nu == -1) {
        Error("Must provide number of control points \"nu\" with NURBS shape.");
        return std::vector<std::shared_ptr<Shape>>();
    }

    int uorder = params.FindOneInt("uorder", -1);
    if (uorder == -1) {
        Error("Must provide u order \"uorder\" with NURBS shape.");
        return std::vector<std::shared_ptr<Shape>>();
    }
    int nuknots, nvknots;
    const Float *uknots = params.FindFloat("uknots", &nuknots);
    if (uknots == nullptr) {
        Error("Must provide u knot vector \"uknots\" with NURBS shape.");
        return std::vector<std::shared_ptr<Shape>>();
    }

    if (nuknots != nu + uorder) {
        Error(
            "Number of knots in u knot vector %d doesn't match sum of "
            "number of u control points %d and u order %d.",
            nuknots, nu, uorder);
        return std::vector<std::shared_ptr<Shape>>();
    }

    Float u0 = params.FindOneFloat("u0", uknots[uorder - 1]);
    Float u1 = params.FindOneFloat("u1", uknots[nu]);

    int nv = params.FindOneInt("nv", -1);
    if (nv == -1) {
        Error("Must provide number of control points \"nv\" with NURBS shape.");
        return std::vector<std::shared_ptr<Shape>>();
    }

    int vorder = params.FindOneInt("vorder", -1);
    if (vorder == -1) {
        Error("Must provide v order \"vorder\" with NURBS shape.");
        return std::vector<std::shared_ptr<Shape>>();
    }

    const Float *vknots = params.FindFloat("vknots", &nvknots);
    if (vknots == nullptr) {
        Error("Must provide v knot vector \"vknots\" with NURBS shape.");
        return std::vector<std::shared_ptr<Shape>>();
    }

    if (nvknots != nv + vorder) {
        Error(
            "Number of knots in v knot vector %d doesn't match sum of "
            "number of v control points %d and v order %d.",
            nvknots, nv, vorder);
        return std::vector<std::shared_ptr<Shape>>();
    }

    Float v0 = params.FindOneFloat("v0", vknots[vorder - 1]);
    Float v1 = params.FindOneFloat("v1", vknots[nv]);

    bool isHomogeneous = false;
    int npts;
    const Float *P = (const Float *)params.FindPoint3f("P", &npts);
    if (!P) {
        P = params.FindFloat("Pw", &npts);
        if (!P) {
            Error(
                "Must provide control points via \"P\" or \"Pw\" parameter to "
                "NURBS shape.");
            return std::vector<std::shared_ptr<Shape>>();
        }
        if ((npts % 4) != 0) {
            Error(
                "Number of \"Pw\" control points provided to NURBS shape must "
                "be "
                "multiple of four");
            return std::vector<std::shared_ptr<Shape>>();
        }
        npts /= 4;
        isHomogeneous = true;
    }
    if (npts != nu * nv) {
        Error("NURBS shape was expecting %dx%d=%d control points, was given %d",
              nu, nv, nu * nv, npts);
        return std::vector<std::shared_ptr<Shape>>();
    }

    // Compute NURBS dicing rates
    int diceu = 30, dicev = 30;
    std::unique_ptr<Float[]> ueval(new Float[diceu]);
    std::unique_ptr<Float[]> veval(new Float[dicev]);
    std::unique_ptr<Point3f[]> evalPs(new Point3f[diceu * dicev]);
    std::unique_ptr<Normal3f[]> evalNs(new Normal3f[diceu * dicev]);
    int i;
    for (i = 0; i < diceu; ++i)
        ueval[i] = Lerp((float)i / (float)(diceu - 1), u0, u1);
    for (i = 0; i < dicev; ++i)
        veval[i] = Lerp((float)i / (float)(dicev - 1), v0, v1);

    // Evaluate NURBS over grid of points
    memset(evalPs.get(), 0, diceu * dicev * sizeof(Point3f));
    memset(evalNs.get(), 0, diceu * dicev * sizeof(Point3f));
    std::unique_ptr<Point2f[]> uvs(new Point2f[diceu * dicev]);

    // Turn NURBS into triangles
    std::unique_ptr<Homogeneous3[]> Pw(new Homogeneous3[nu * nv]);
    if (isHomogeneous) {
        for (int i = 0; i < nu * nv; ++i) {
            Pw[i].x = P[4 * i];
            Pw[i].y = P[4 * i + 1];
            Pw[i].z = P[4 * i + 2];
            Pw[i].w = P[4 * i + 3];
        }
    } else {
        for (int i = 0; i < nu * nv; ++i) {
            Pw[i].x = P[3 * i];
            Pw[i].y = P[3 * i + 1];
            Pw[i].z = P[3 * i + 2];
            Pw[i].w = 1.;
        }
    }

    for (int v = 0; v < dicev; ++v) {
        for (int u = 0; u < diceu; ++u) {
            uvs[(v * diceu + u)].x = ueval[u];
            uvs[(v * diceu + u)].y = veval[v];

            Vector3f dpdu, dpdv;
            Point3f pt = NURBSEvaluateSurface(uorder, uknots, nu, ueval[u],
                                              vorder, vknots, nv, veval[v],
                                              Pw.get(), &dpdu, &dpdv);
            evalPs[v * diceu + u].x = pt.x;
            evalPs[v * diceu + u].y = pt.y;
            evalPs[v * diceu + u].z = pt.z;
            evalNs[v * diceu + u] = Normal3f(Normalize(Cross(dpdu, dpdv)));
        }
    }

    // Generate points-polygons mesh
    int nTris = 2 * (diceu - 1) * (dicev - 1);
    std::unique_ptr<int[]> vertices(new int[3 * nTris]);
    int *vertp = vertices.get();
    // Compute the vertex offset numbers for the triangles
    for (int v = 0; v < dicev - 1; ++v) {
        for (int u = 0; u < diceu - 1; ++u) {
#define VN(u, v) ((v)*diceu + (u))
            *vertp++ = VN(u, v);
            *vertp++ = VN(u + 1, v);
            *vertp++ = VN(u + 1, v + 1);

            *vertp++ = VN(u, v);
            *vertp++ = VN(u + 1, v + 1);
            *vertp++ = VN(u, v + 1);
#undef VN
        }
    }
    int nVerts = diceu * dicev;

    return CreateTriangleMesh(o2w, w2o, reverseOrientation, nTris,
                              vertices.get(), nVerts, evalPs.get(), nullptr,
                              evalNs.get(), uvs.get(), nullptr);
}
Example #6
0
    void PerlinNoise::execute_attributes(const CompositeDataBuffer &input, DataBuffer &output, DataBuffer::size_type index) const
    {
        PerlinNoiseData& data = *output.get_scratch_ref<std::shared_ptr<PerlinNoiseData>>(0);
        int dimensions = data.dimensions;

        const float* attribute_point = get_input_point_attribute(index, input, dimensions);

        std::vector<int> dimension_starts(dimensions);
        std::vector<std::vector<int>> points;
        std::vector<int> gradient_indexes;

        int num_gradients = data.vectors.size() / dimensions;

        // Get floor(x), floor(y), floor(z) etc
        for(int i = 0; i < dimensions; i++)
        {
            dimension_starts[i] = std::floor(attribute_point[i]);
        }

        // Get all points in the hypercube surrounding the input point
        get_point_permutations(dimension_starts, dimensions, std::vector<int>(), points);

        // Get all rng gradients for each point. Hash x/y/z to get index into gradients array
        for(unsigned int i = 0; i < points.size(); i++)
        {
            std::vector<int>& point = points[i];
            unsigned int sum = 0;
            for(int j = 0; j < dimensions; j++)
            {
                static const unsigned int primes[] { 1699, 2237, 2671, 3571, 1949, 2221, 3469, 3083 };
                sum += point[j] * point[j] * primes[j]; //Hopefully this has enough entropy and random enough results
            }

            gradient_indexes.push_back(sum % num_gradients);
        }

        // Because of how the get_point_permutations algorithm works, adjacent verticies are in pairs recursively
        // There will always be an even number of points (2^n)
        std::vector<float> out(points.size());

        // vector from the first hypercube vertex to the input point (main diagonal) i.e. uv
        std::vector<float> uvs(dimensions);

        for(int i = 0; i < dimensions; i++)
        {
            uvs[i] = attribute_point[i] - points[0][i];
        }

        // Number of vertexes for an n-dimensional hypercube is 2^n
        int num_points = std::pow(2, dimensions);

        // Find dot product for every vertex.
        // Multiply the vector from the hypercube vertex to the input point, and the gradient at the hypercube vertex
        for(int i = 0; i < num_points; i++)
        {
            std::vector<int>& point = points[i];
            int gradient_index = gradient_indexes[i] * dimensions;

            // In theory, the dot products will be -1 to 1
            float dot = 0;

            for(int j = 0; j < dimensions; j++)
            {
                // gradient value (for this dimension) at hypercube vertex
                signed char gradient_u = data.vectors[gradient_index + j];

                // input point (in this dimension)
                float attr_p = attribute_point[j];

                // vector (for this dimension) from hypercube vertex to input point
                float u = attr_p - point[j];

                dot += u * gradient_u;
            }

            out[i] = dot;
        }

        // Collapse the dimensions down
        int current_dimension = dimensions;
        for(; num_points > 1; num_points /= 2)
        {
            // Take the dot products two at a time and interpolate between then
            int half_num = num_points / 2;
            for(int i = 0; i < half_num; i++)
            {
                int index = i * 2;

                float x_1 = out[index];
                float x_2 = out[index + 1];

                float u = uvs[current_dimension - 1];
                float interpolated = 6 * std::pow(u, 5) - (15 * std::pow(u, 4)) + (10 * std::pow(u, 3));

                out[i] = (x_1 * (1.0f - interpolated)) + (x_2 * interpolated);
            }

            current_dimension--;
        }

        output.set_attribute<float, 1>(*output_socket_, index, &out[0]);
    }
SGridder::SGridder(SGridderConfig cfg) {
  // ** Input
  scale = Param<double>("scale");
  grid_size = Param<int>("grid_size");
  vis = ImageParam(type_of<double>(), 2, "vis");

  // GCF: Array of OxOxSxS complex numbers. We "fuse" two dimensions
  // as Halide only supports up to 4 dimensions.
  gcf_fused = ImageParam(type_of<double>(), 4, "gcf");

  // ** Output

  // Grid starts out undefined so we can update the output buffer
  F(uvg);
  uvg(cmplx, x, y) = undef<double>();

  // Get grid limits. This limits the uv pixel coordinates we accept
  // for the top-left corner of the GCF.
  Expr min_u = uvg.output_buffer().min(1);
  Expr max_u = uvg.output_buffer().min(1) + uvg.output_buffer().extent(1) - cfg.gcfSize - 1;
  Expr min_v = uvg.output_buffer().min(2);
  Expr max_v = uvg.output_buffer().min(2) + uvg.output_buffer().extent(2) - cfg.gcfSize - 1;

  // ** Helpers

  // Coordinate preprocessing
  Func Q(uvs);
  F(uv), F(overc);
  uvs(uvdim, t) = vis(uvdim, t) * scale;
  overc(uvdim, t) = clamp(cast<int>(round(OVER * (uvs(uvdim, t) - floor(uvs(uvdim, t))))), 0, OVER-1);
  uv(uvdim, t) = cast<int>(floor(uvs(uvdim, t)) + grid_size / 2 - cfg.gcfSize / 2);

  // Visibilities to ignore due to being out of bounds
  F(inBound);
  inBound(t) = uv(_U, t) >= min_u && uv(_U, t) <= max_u &&
               uv(_V, t) >= min_v && uv(_V, t) <= max_v;

  // GCF lookup for a given visibility
  Func Q(gcf);
  Var suppx("suppx"), suppy("suppy"), overx("overx"), overy("overy");
  gcf(suppx, suppy, t)
      = Complex(gcf_fused(_REAL, suppx, suppy, overc(_U, t) + OVER * overc(_V, t)),
                gcf_fused(_IMAG, suppx, suppy, overc(_U, t) + OVER * overc(_V, t)));

  // ** Definition

  // Reduction domain. Note that we iterate over time steps before
  // switching the GCF row in order to increase locality (Romein).
  typedef std::pair<Expr, Expr> rType;
  rType
      cRange = {0, _CPLX_FIELDS}
    , gRange = {0, cfg.gcfSize}
    , vRange = {0, cfg.steps}
    , blRange = {0, vis.height() / cfg.steps}
    ;

  std::vector<rType> rVec(5);
  rVec[cfg.cpos] = cRange;
  rVec[cfg.xpos] = gRange;
  rVec[cfg.ypos] = gRange;
  rVec[cfg.vpos] = vRange;
  rVec[cfg.blpos] = blRange;

  RDom red(rVec);
    rcmplx = red[cfg.cpos]
  , rgcfx  = red[cfg.xpos]
  , rgcfy  = red[cfg.ypos]
  , rstep  = red[cfg.vpos]
  , rbl    = red[cfg.blpos]
  ;
  Expr rvis = vis.top() + cfg.steps * rbl + rstep;

  // Get visibility as complex number
  Complex visC(vis(_R, rvis), vis(_I, rvis));

  // Update grid
  uvg(rcmplx,
      rgcfx + clamp(uv(_U, rvis), min_u, max_u),
      rgcfy + clamp(uv(_V, rvis), min_v, max_v))
    += select(inBound(rvis),
              (visC * Complex(gcf(rgcfx, rgcfy, rvis))).unpack(rcmplx),
              undef<double>());

  if (cfg.dim & (1 << _VIS0)) vis.set_min(0,0).set_stride(0,1).set_extent(0,_VIS_FIELDS);
  if (cfg.dim & (1 << _VIS1)) vis.set_stride(1,_VIS_FIELDS);

  if (cfg.dim & (1 << _GCF0)) gcf_fused.set_min(0,0).set_stride(0,1).set_extent(0,_CPLX_FIELDS);
  if (cfg.dim & (1 << _GCF1)) gcf_fused.set_min(1,0).set_stride(1,_CPLX_FIELDS).set_extent(1,cfg.gcfSize);
  if (cfg.dim & (1 << _GCF2)) gcf_fused.set_min(2,0).set_stride(2,_CPLX_FIELDS*cfg.gcfSize).set_extent(2,cfg.gcfSize);
  if (cfg.dim & (1 << _GCF3)) gcf_fused.set_min(3,0).set_stride(3,_CPLX_FIELDS*cfg.gcfSize*cfg.gcfSize).set_extent(3,OVER*OVER);

  if (cfg.dim & (1 << _UVG0)) uvg.output_buffer().set_stride(0,1).set_extent(0,_CPLX_FIELDS);
  if (cfg.dim & (1 << _UVG1)) uvg.output_buffer().set_stride(1,_CPLX_FIELDS);

  // Compute UV & oversampling coordinates per visibility
  overc.compute_at(uvg, rstep).vectorize(uvdim);
  uv.compute_at(uvg, rstep).vectorize(uvdim);
  inBound.compute_at(uvg, rstep);

  RVar rgcfxc("rgcfxc");
  switch(cfg.upd)
  {
  case _UPD_NONE:
      break;
  case _UPD_VECT:
      uvg.update()
          .allow_race_conditions()
          .vectorize(rcmplx);
      break;
  case _UPD_FUSE:
      uvg.update()
          .allow_race_conditions()
          .fuse(rgcfx, rcmplx, rgcfxc)
          .vectorize(rgcfxc, cfg.vector);
      break;
  case _UPD_FUSE_UNROLL:
      uvg.update()
          .allow_race_conditions()
          .fuse(rgcfx, rcmplx, rgcfxc)
          .vectorize(rgcfxc, cfg.vector)
          .unroll(rgcfxc, cfg.gcfSize * 2 / cfg.vector);
      break;
  case _UPD_UNROLL:
      uvg.update()
          .unroll(rcmplx);
      break;
  }
}
void vixo_hairStyleMaya::exportNoise(MString uvInfo,MString noiseInfo,MDataBlock& data)
{
	//getUVinfo
	ifstream fin(uvInfo.asChar(),ios_base::in|ios_base::binary);

	int triNum;
	fin.read((char*)&triNum,sizeof(int));

	vector<int> hairNumPerTri(triNum);
	fin.read((char*)&hairNumPerTri[0],sizeof(int)*triNum);

	vector<int> ctrlVert(triNum*3);
	fin.read((char*)&ctrlVert[0],sizeof(int)*triNum*3);

	vector<int> hairStartIdxPerTri(triNum,0);
	int hairNum=0;
	for(int i=1;i<triNum;i++)
	{
		hairStartIdxPerTri[i]=hairStartIdxPerTri[i-1]+hairNumPerTri[i-1];
	}
	hairNum=hairStartIdxPerTri[triNum-1]+hairNumPerTri[triNum-1];

	vector<float> uvs(hairNum*2);
	fin.read((char*)&uvs[0],sizeof(float)*hairNum*2);
	fin.close();

	//get noise data
	float lengthNoiseAmpValue=data.inputValue(lengthNoiseAmp).asFloat();
	float lengthNoiseFreqValue=data.inputValue(lengthNoiseFreq).asFloat();
	float inclinationNoiseAmpValue=data.inputValue(inclinationNoiseAmp).asFloat();
	float inclinationNoiseFreqValue=data.inputValue(inclinationNoiseFreq).asFloat();
	float polarNoiseAmpValue=data.inputValue(polarNoiseAmp).asFloat();
	float polarNoiseFreqValue=data.inputValue(polarNoiseFreq).asFloat();
	float tipCurlNoiseAmpValue=data.inputValue(tipCurlNoiseAmp).asFloat();
	float tipCurlNoiseFreqValue=data.inputValue(tipCurlNoiseFreq).asFloat();
	float baseCurlNoiseAmpValue=data.inputValue(baseCurlNoiseAmp).asFloat();
	float baseCurlNoiseFreqValue=data.inputValue(baseCurlNoiseFreq).asFloat();
	//~get noise data
	const float PI=3.1415926;
	vector<follicleNoise> hairNoise(hairNum);
	for(int i=0;i<hairNum;i++)
	{
		float lenn=noise::atPointUV(uvs[2*i]*lengthNoiseFreqValue,uvs[2*i+1]*lengthNoiseFreqValue);
		hairNoise[i].length=(lenn*2-1)*lengthNoiseAmpValue+1;
		hairNoise[i].inclination=(noise::atPointUV(uvs[2*i]*inclinationNoiseFreqValue,uvs[2*i+1]*inclinationNoiseFreqValue)*2-1)*lengthNoiseAmpValue*PI/2;
		hairNoise[i].polar=(noise::atPointUV(uvs[2*i]*polarNoiseFreqValue,uvs[2*i+1]*polarNoiseFreqValue)*2-1)*polarNoiseAmpValue*PI;
		hairNoise[i].tipCurl=(noise::atPointUV(uvs[2*i]*tipCurlNoiseFreqValue,uvs[2*i+1]*tipCurlNoiseFreqValue)*2-1)*tipCurlNoiseAmpValue*PI/6;
		hairNoise[i].baseCurl=(noise::atPointUV(uvs[2*i]*baseCurlNoiseFreqValue,uvs[2*i+1]*baseCurlNoiseFreqValue)*2-1)*baseCurlNoiseAmpValue*PI/6;
	}

	vector<streampos> startPos(triNum);
	startPos[0]=sizeof(int)+sizeof(streampos)*triNum;
	for(int i=1;i<triNum;i++)
	{
		startPos[i]=startPos[i-1].operator+(sizeof(int)+sizeof(follicleNoise)*hairNumPerTri[i-1]);
	}

	//print noise info
	fstream fout(noiseInfo.asChar(),ios_base::out|ios_base::trunc|ios_base::binary);
	fout.write((char*)&triNum,sizeof(int));
	fout.write((char*)&startPos[0],sizeof(streampos)*triNum);
	for(int i=0;i<triNum;i++)
	{
		fout.write((char*)&hairNumPerTri[i],sizeof(int));
		fout.write((char*)&hairNoise[hairStartIdxPerTri[i]],sizeof(follicleNoise)*hairNumPerTri[i]);
	}
	fout.flush();
	fout.close();
	//~print noise info

	//debug
	/*
	noiseInfo=noiseInfo+"debug";
	fout.open(noiseInfo.asChar(),ios_base::out|ios_base::trunc);
	fout<<triNum<<endl;
	for(int i=0;i<triNum;i++)
	{
		fout<<i<<":"<<startPos[i]<<'\t'<<hairNumPerTri[i]<<endl;
		for(int j=0;j<hairNumPerTri[i];j++)
		{
			fout<<'\t'<<j<<":"<<hairNoise[hairStartIdxPerTri[i]+j].length
				<<'\t'<<hairNoise[hairStartIdxPerTri[i]+j].inclination
				<<'\t'<<hairNoise[hairStartIdxPerTri[i]+j].polar
				<<'\t'<<hairNoise[hairStartIdxPerTri[i]+j].tipCurl
				<<'\t'<<hairNoise[hairStartIdxPerTri[i]+j].baseCurl<<endl;
		}
	}
	fout.flush();
	fout.close();
	*/
	//~debug
}
void vixo_hairStyleMaya::exportColor(MString uvInfo,MString colorInfo,MDataBlock& data)
{
	//getUVinfo
	ifstream fin(uvInfo.asChar(),ios_base::in|ios_base::binary);

	int triNum;
	fin.read((char*)&triNum,sizeof(int));

	vector<int> hairNumPerTri(triNum);
	fin.read((char*)&hairNumPerTri[0],sizeof(int)*triNum);

	vector<int> ctrlVert(triNum*3);
	fin.read((char*)&ctrlVert[0],sizeof(int)*triNum*3);

	vector<int> hairStartIdxPerTri(triNum,0);
	int hairNum=0;
	for(int i=1;i<triNum;i++)
	{
		hairStartIdxPerTri[i]=hairStartIdxPerTri[i-1]+hairNumPerTri[i-1];
	}
	hairNum=hairStartIdxPerTri[triNum-1]+hairNumPerTri[triNum-1];

	vector<float> uvs(hairNum*2);
	fin.read((char*)&uvs[0],sizeof(float)*hairNum*2);
	fin.close();


	//getTextureInfo
	vector<follicleColor> follicleInfo(hairNum);
	getTextureInfo("densityMap",uvs,follicleInfo,data);
	getTextureInfo("tipColor",uvs,follicleInfo,data);
	getTextureInfo("baseColor",uvs,follicleInfo,data);
	getTextureInfo("tipOpacity",uvs,follicleInfo,data);
	getTextureInfo("baseOpacity",uvs,follicleInfo,data);

	vector<streampos> startPos(triNum);
	startPos[0]=sizeof(int)+sizeof(streampos)*triNum;
	for(int i=1;i<triNum;i++)
	{
		startPos[i]=startPos[i-1].operator+(sizeof(int)+sizeof(follicleColor)*hairNumPerTri[i-1]);
	}

	//Êä³ö
	fstream fout(colorInfo.asChar(),ios_base::out|ios_base::trunc|ios_base::binary);
	fout.write((char*)&triNum,sizeof(int));
	fout.write((char*)&startPos[0],sizeof(streampos)*triNum);
	for(int i=0;i<triNum;i++)
	{
		fout.write((char*)&hairNumPerTri[i],sizeof(int));
		fout.write((char*)&follicleInfo[hairStartIdxPerTri[i]],sizeof(follicleColor)*hairNumPerTri[i]);
	}
	fout.flush();
	fout.close();

	//Êä³ö
	/*
	colorInfo=colorInfo+"debug";
	fout.open(colorInfo.asChar(),ios_base::out|ios_base::trunc);
	fout<<triNum<<endl;
	for(int i=0;i<triNum;i++)
	{
		fout<<i<<":"<<startPos[i]<<'\t'<<hairNumPerTri[i]<<endl;
		for(int j=0;j<hairNumPerTri[i];j++)
		{
			fout<<'\t'<<j<<":"<<follicleInfo[hairStartIdxPerTri[i]+j].density<<'\t'<<follicleInfo[hairStartIdxPerTri[i]+j].tipColorB<<endl;
		}
	}
	fout.flush();
	fout.close();
	*/
}
void vixo_hairStyleMaya::exportFollicle(int hairNumValue,MString uvFileName,MString hairRootInfo,MDataBlock& data)
{
	MObject meshObj=data.inputValue(inMesh).asMesh();
		
	MItMeshPolygon iterFace(meshObj);
	double area=0.0;
	int triTotNum=0;
	for(iterFace.reset();!iterFace.isDone();iterFace.next())
	{
		double areaTmp;
		iterFace.getArea(areaTmp);
		area+=areaTmp;
		int triTotNumTmp;
		iterFace.numTriangles(triTotNumTmp);
		triTotNum+=triTotNumTmp;

		if(iterFace.isDone())
			break;
	}
	MFnMesh fnMesh(meshObj);

	vector<vector<float>> uv(triTotNum);
	vector<int> triInfoIndex(triTotNum,0); //numFollicle
	vector<streampos> triInfoPosIndex(triTotNum);
	vector<int> vertexCtrl(triTotNum*3);
	vector<vector<float>> vertexWeight(triTotNum);
	
	double diff=sqrt(area/hairNumValue);

	int hairIter=0;
	int triIter=0;
	for(iterFace.reset();!iterFace.isDone();iterFace.next())
	{
		MIntArray faceVertexIndex;
		iterFace.getVertices(faceVertexIndex);

		MVectorArray faceNormals;
		iterFace.getNormals(faceNormals,MSpace::kWorld);

		MFloatVectorArray faceTangents;
		fnMesh.getFaceVertexTangents(iterFace.index(),faceTangents,MSpace::kWorld);

		map<int,int> vertexIndex2faceIndex;
		for(int i=0;i<faceVertexIndex.length();i++)
		{
			vertexIndex2faceIndex.insert(pair<int,int>(faceVertexIndex[i],i));
		}
		int triNum;
		iterFace.numTriangles(triNum);
		MString uvset("map1");
		for(int tri=0;tri<triNum;tri++)
		{
			MPointArray vertex;
			MIntArray vertexIndex;
			iterFace.getTriangle(tri,vertex,vertexIndex);
			int numLine1=MVector(vertex[1]-vertex[0]).length()/diff;
			int numLine2=MVector(vertex[2]-vertex[0]).length()/diff;

			MFloatArray uvs(6);
			float2 tempUV;
			iterFace.getUV(vertexIndex2faceIndex.find(vertexIndex[0])->second,tempUV,&uvset);
			uvs[0]=tempUV[0];
			uvs[1]=tempUV[1];
			iterFace.getUV(vertexIndex2faceIndex.find(vertexIndex[1])->second,tempUV,&uvset);
			uvs[2]=tempUV[0];
			uvs[3]=tempUV[1];
			iterFace.getUV(vertexIndex2faceIndex.find(vertexIndex[2])->second,tempUV,&uvset);
			uvs[4]=tempUV[0];
			uvs[5]=tempUV[1];

			vertexCtrl[3*triIter]=vertexIndex[1];
			vertexCtrl[3*triIter+1]=vertexIndex[2];
			vertexCtrl[3*triIter+2]=vertexIndex[0];

			

			for(int i=0;i<numLine1;i++)
			{
				for(int j=0;j<(1-(float)i/numLine1)*numLine2;j++)
				{
					vertexWeight[triIter].push_back((float)i/numLine1);
					vertexWeight[triIter].push_back((float)j/numLine2);
					vertexWeight[triIter].push_back(1-(float)i/numLine1-(float)j/numLine2);

					int localHairIter=triInfoIndex[triIter];

					uv[triIter].push_back(uvs[0]*vertexWeight[triIter][3*localHairIter+2]+uvs[2]*vertexWeight[triIter][3*localHairIter]+uvs[4]*vertexWeight[triIter][3*localHairIter+1]);
					uv[triIter].push_back(uvs[1]*vertexWeight[triIter][3*localHairIter+2]+uvs[3]*vertexWeight[triIter][3*localHairIter]+uvs[5]*vertexWeight[triIter][3*localHairIter+1]);
					
					hairIter++;
					triInfoIndex[triIter]++;
				}
			}

			triIter++;
		}
		if(iterFace.isDone())
			break;
	}

	//print uv
	fstream fout(uvFileName.asChar(),ios_base::out|ios_base::binary);
	fout.write((char*)&triTotNum,sizeof(int));
	fout.write((char*)&triInfoIndex[0],sizeof(int)*triTotNum);
	fout.write((char*)&vertexCtrl[0],sizeof(int)*3*triTotNum); //for cache export
	for(int i=0;i<triTotNum;i++) //for color info
	{
		fout.write((char*)&uv[i][0],sizeof(float)*2*triInfoIndex[i]);
	}
	fout.flush();
	fout.close();
	//~print uv

	/*
	//debug uv
	MString uvDebug=uvFileName+"debug";
	fout.open(uvDebug.asChar(),ios_base::out);
	fout<<triTotNum<<endl;
	for(int i=0;i<triTotNum;i++)
		fout<<"tri"<<i<<":"<<'\t'<<triInfoIndex[i]<<'\t'<<vertexCtrl[3*i]<<'\t'<<vertexCtrl[3*i+1]<<'\t'<<vertexCtrl[3*i+2]<<endl;
	for(int i=0;i<triTotNum;i++)
		for(int j=0;j<triInfoIndex[i];j++)
			fout<<"tri"<<i<<":"<<'\t'<<j<<'\t'<<uv[i][2*j]<<'\t'<<uv[i][2*j+1]<<endl;
	fout.flush();
	fout.close();
	//~debug uv
	*/
	//prepare streampos
	triInfoPosIndex[0]=sizeof(int)+sizeof(streampos)*triTotNum;
	for(int i=1;i<triTotNum;i++)
	{
		triInfoPosIndex[i]=triInfoPosIndex[i-1].operator+(sizeof(int)+sizeof(float)*3*triInfoIndex[i-1]);
	}
	//~prepare streampos
	
	//print infos
	fout.open(hairRootInfo.asChar(),ios_base::out|ios_base::binary);
	fout.write((char*)&triTotNum,sizeof(int));
	fout.write((char*)&triInfoPosIndex[0],sizeof(streampos)*triTotNum);
	for(int i=0;i<triTotNum;i++) //for color info
	{
		fout.write((char*)&triInfoIndex[i],sizeof(int));
		fout.write((char*)&vertexWeight[i][0],sizeof(float)*3*triInfoIndex[i]);
	}
	fout.flush();
	fout.close();
	//~print infos
	/*
	//debug info
	MString hairRootInfoDebug=hairRootInfo+"debug";
	fout.open(hairRootInfoDebug.asChar(),ios_base::out);
	fout<<triTotNum<<endl;
	for(int i=0;i<triTotNum;i++)
		fout<<"tri"<<i<<":"<<'\t'<<triInfoIndex[i]<<'\t'<<triInfoPosIndex[i]<<endl;
	for(int i=0;i<triTotNum;i++)
		for(int j=0;j<triInfoIndex[i];j++)
			fout<<"tri"<<i<<":"<<'\t'<<j<<'\t'<<vertexWeight[i][3*j]<<'\t'<<vertexWeight[i][3*j+1]<<'\t'<<vertexWeight[i][3*j+2]<<endl;
	fout.flush();
	fout.close();
	//~debug info
	*/
}