//-----------------------------------------------------------------------
	void MaterialService::loadFlowTextures(const FileGroupPtr& db) {
		// Load the TXLIST chunk from the resource mission file.
		Opde::FilePtr flow_tex;
		try {
			flow_tex = db->getFile("FLOW_TEX");
		} catch (FileException) {
			LOG_ERROR("Flow chunk does not exist. Water materials may not be correctly displayed",
					"MaterialService::loadFlowTextures");
			return;
		}

		// TODO: Exception handling on the chunk readout!
		// Okay, we are ready to map the arrays
		DarkDBChunkFLOW_TEX flows;
		int flow_count = flow_tex->size() / 32; // The record is 32 bytes long, this way we do not fail if the chunk is shorter

		try {
			// load
			flow_tex->read(&flows, flow_tex->size()); // To be sure we do not overlap
		} catch (Ogre::Exception& e) {
			// Connect the original exception to the printout:
			OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, String("Could not load flow textures : ")
					+ e.getFullDescription(), "MaterialService::loadFlowTextures");
		}


		// now try to load non-zero flow textures as materials
		for (int fnum = 0; fnum < flow_count; fnum++) {
			if (strlen(flows.flow[fnum].name) > 0) { // nonzero name, try to load
				// Construct the basic name of the material
				std::string matname("water/");
				matname += flows.flow[fnum].name;

				LOG_INFO("MaterialService::loadFlowTextures: Loading flow %d : %d/%d '%s'", fnum, flows.flow[fnum].in_texture,  flows.flow[fnum].out_texture, matname.c_str());

				// try to find the texture definition. If found, clone to the @template + the in_texture/out_texture number
				if (MaterialManager::getSingleton().resourceExists(matname + "_in")) {
					MaterialPtr origMat = MaterialManager::getSingleton().getByName(matname + "_in");

					StringUtil::StrStreamType strb;
					strb << "@template" << flows.flow[fnum].in_texture;

					std::string templn(strb.str());

					if (MaterialManager::getSingleton().resourceExists(templn)) {
						MaterialManager::getSingleton().remove(templn);
					}

					MaterialPtr shadMat = origMat->clone(templn);
					shadMat->load();

					addWorldMaterialTemplate(flows.flow[fnum].in_texture, shadMat);
					LOG_INFO("Flow now defined : %s (template %s_in)", templn.c_str(), matname.c_str());
				} else {
					LOG_ERROR("Material not found : %s_in", matname.c_str());
				}


				// OUT
				if (MaterialManager::getSingleton().resourceExists(matname + "_out")) {
					MaterialPtr origMat = MaterialManager::getSingleton().getByName(matname + "_out");

					StringUtil::StrStreamType strb;
					strb << "@template" << flows.flow[fnum].in_texture;

					std::string templn(strb.str());

					if (MaterialManager::getSingleton().resourceExists(templn)) {
						MaterialManager::getSingleton().remove(templn);
					}

					MaterialPtr shadMat = origMat->clone(templn);
					shadMat->load();

					addWorldMaterialTemplate(flows.flow[fnum].out_texture, shadMat);
					LOG_INFO("Flow now defined : %s (template %s_in)", templn.c_str(), matname.c_str());
				} else {
					LOG_ERROR("Material not found : %s_out", matname.c_str());
				}

			}
		}
	}
vtkDataArray *
avtMatvfExpression::DeriveVariable(vtkDataSet *in_ds, int currentDomainsIndex)
{
    int    i, j;

    int ncells = in_ds->GetNumberOfCells();

    //
    // The 'currentDomainsIndex' is a data member of the base class that is
    // set to be the id of the current domain right before DeriveVariable is
    // called.  We need that index to make sure we are getting the right mat.
    //
    // The 'currentTimeState' is a data member of the base class that is
    // set to be the current timestep during ExamineContract. 
    // We need that timestep to make sure we are getting the right mat.
    //
    // doPostGhost allows us to request ghost corrected material data 
    // if necessary.
    //

    // only ask for post ghost Material info if the dataset actually
    // has ghost zones and VisIt created them. 

    avtDataAttributes &datts = GetInput()->GetInfo().GetAttributes();
    bool created_ghosts = datts.GetContainsGhostZones() == AVT_CREATED_GHOSTS;
    // check bitmask to make sure we actually have bondary ghost zones
    // (in some cases we may only have nesting ghosts zones, and post ghost
    // is not the proper path)
    if(created_ghosts)
        created_ghosts = datts.GetGhostZoneTypesPresent() & AVT_BOUNDARY_GHOST_ZONES;

    doPostGhost = doPostGhost  &&
                  in_ds->GetCellData()->GetArray("avtGhostZones") &&
                  created_ghosts;
    debug5 << "avtMatvfExpression: GetGhostZoneTypesPresent() = "
           << datts.GetGhostZoneTypesPresent() <<  endl;


    debug5 << "avtMatvfExpression: Using post ghost material object ?  "
           << doPostGhost <<  endl;

    avtMaterial *mat = GetMetaData()->GetMaterial(currentDomainsIndex,
                                                  currentTimeState,
                                                  doPostGhost);

    if (mat == NULL)
    {
        debug1 << "Could not find a material object." << endl;
        vtkDoubleArray *dummy = vtkDoubleArray::New();
        dummy->SetNumberOfTuples(ncells);
        for (i = 0 ; i < ncells ; i++)
            dummy->SetTuple1(i, 0.);
        return dummy;
    }

    //
    // Note that we are setting up vf_for_orig_cells based on the number of
    // zones in the original dataset -- this may or may not be the number
    // of cells in the input, depending on whether or not we did MIR.
    //
    vtkDoubleArray *vf_for_orig_cells = vtkDoubleArray::New();
    int norigcells = mat->GetNZones();
    vf_for_orig_cells->SetNumberOfTuples(norigcells);

    //
    // Try to match up the materials in the avtMaterial object with the
    // materials requested by the users.
    //
    int nmats = mat->GetNMaterials();
    std::vector<bool>  useMat(nmats, false);
    std::vector<bool>  matchedMatName(matNames.size(), false);
    std::vector<bool>  matchedMatIndex(matIndices.size(), false);
    for (i = 0 ; i < nmats ; i++)
    {
        std::string currentMat = mat->GetMaterials()[i];
        for (j = 0 ; j < matNames.size() ; j++)
        {
            if (currentMat == matNames[j])
            {
                useMat[i] = true;
                matchedMatName[j] = true;
            }
        }
        for (j = 0 ; j < matIndices.size() ; j++)
        {
            char tmp[256];
            sprintf(tmp, "%d", matIndices[j]);

            std::string matname(tmp);
            if (currentMat == matname ||
                (currentMat.length() > matname.length() &&
                 currentMat.substr(0,matname.length() + 1) == (matname + " ")))
            {
                useMat[i] = true;
                matchedMatIndex[j] = true;
            }
        }
    }

    //
    // Make sure that we found every material requested.  If not, issue
    // a warning.
    //
    for (i = 0 ; i < matNames.size() ; i++)
    {
        if (!matchedMatName[i])
        {
            const std::vector<std::string> &all_mats = 
                                                mat->GetCompleteMaterialList();
            bool matched = false;
            for (j = 0 ; j < all_mats.size() ; j++)
            {
                if (matNames[i] == all_mats[j])
                {
                    matched = true;
                    break;
                }
            }
            if (!matched)
            {
                if (!issuedWarning)
                {
                    char warningString[100000];
                    sprintf(warningString, "Could not match up \"%s\" with "
                              "any materials when doing the matvf expression."
                              "\nList of valid materials is: ", 
                              matNames[i].c_str());
                    char *tmp = warningString + strlen(warningString);
                    for (j = 0 ; j < all_mats.size() ; j++)
                    {
                        if (j < (all_mats.size()-1))
                            sprintf(tmp, "\"%s\", ", all_mats[j].c_str());
                        else
                            sprintf(tmp, "\"%s\".", all_mats[j].c_str());
                        tmp += strlen(tmp);
                    }
                    avtCallback::IssueWarning(warningString);
                    issuedWarning = true;
                }
            }
        }
    }
    for (i = 0 ; i < matIndices.size() ; i++)
    {
        char tmp[256];
        sprintf(tmp, "%d", matIndices[i]);

        std::string matname(tmp);
        if (!matchedMatIndex[i])
        {
            const std::vector<std::string> &all_mats = 
                                                mat->GetCompleteMaterialList();
            bool matched = false;
            for (j = 0 ; j < all_mats.size() ; j++)
            {
                if (matname == all_mats[j])
                {
                    matched = true;
                    break;
                }
            }
            if (!matched)
            {
                if (!issuedWarning)
                {
                    char warningString[100000];
                    sprintf(warningString, "Could not match up \"%s\" with "
                              "any materials when doing the matvf expression."
                              "\nList of valid materials is: ", 
                              matname.c_str());
                    char *tmp = warningString + strlen(warningString);
                    for (j = 0 ; j < all_mats.size() ; j++)
                    {
                        if (j < (all_mats.size()-1))
                            sprintf(tmp, "\"%s\", ", all_mats[j].c_str());
                        else
                            sprintf(tmp, "\"%s\".", all_mats[j].c_str());
                        tmp += strlen(tmp);
                    }
                    avtCallback::IssueWarning(warningString);
                    issuedWarning = true;
                }
            }
        }
    }
    
    //
    // Walk through the material data structure and calculate the volume
    // fraction for each cell.
    //
    const int *matlist = mat->GetMatlist();
    const int *mixmat = mat->GetMixMat();
    const float *mixvf = mat->GetMixVF();
    const int *mix_next = mat->GetMixNext();
    for (i = 0 ; i < norigcells ; i++)
    {
        double vf = 0.;
        if (matlist[i] >= 0)
        {
            vf = (useMat[matlist[i]] ? 1. : 0.);
        }
        else
        {
            vf = 0.;
            int current = -matlist[i]-1;

            // iterations < 1000 just to prevent infinite loops if someone
            // set this structure up wrong.
            int iterations = 0;
            bool stillMoreMats = true;
            while (stillMoreMats && (iterations < 1000))
            {
                if (useMat[mixmat[current]])
                {
                    vf += mixvf[current];
                }
                if (mix_next[current] == 0)
                    stillMoreMats = false;
                else
                    current = mix_next[current]-1;
                iterations++;
            }
        }
        vf_for_orig_cells->SetTuple1(i, vf);
    }

    bool zonesMatchMaterialObject = GetInput()->GetInfo().GetValidity().
                                            GetZonesPreserved();
    vtkDoubleArray *rv = NULL;
    if (zonesMatchMaterialObject)
    {
        //
        // We have the volume fractions for the original cells and we are
        // operating on the original cells -- we're done.
        //
        rv = vf_for_orig_cells;
        rv->Register(NULL); // Because vf_for_orig_cells will be deleted later.

        // Sanity check.
        if (norigcells != ncells)
            EXCEPTION0(ImproperUseException);
    }
    else
    {
        //
        // We have the volume fractions for the original cells, but the
        // original cells have been modified -- most likely by MIR.  Use
        // their original indexing to determine the volume fractions.
        //
        rv = vtkDoubleArray::New();
        rv->SetNumberOfTuples(ncells);

        vtkUnsignedIntArray *ids = (vtkUnsignedIntArray *)
                      in_ds->GetCellData()->GetArray("avtOriginalCellNumbers");
        if (ids == NULL)
        {
            EXCEPTION0(ImproperUseException);
        }
        int ncomps = ids->GetNumberOfComponents();
        unsigned int *ptr = ids->GetPointer(0);
        for (i = 0 ; i < ncells ; i++)
        {
             //
             // The id's are poorly arranged.  There may be one or two
             // components -- always with zones, sometimes with domains.
             // The zones are always the last --> ncomps-1 st component.
             //
             unsigned int id = ptr[ncomps*i + (ncomps-1)];
             rv->SetTuple1(i, vf_for_orig_cells->GetTuple1(id));
        }

    }

    // This was essentially a temporary for us.
    vf_for_orig_cells->Delete();

    return rv;
}