bool SpeckleyElements::initFromSpeckley(const speckley::SpeckleyDomain* dom, int fsType)
{
    if (fsType != speckley::Elements) {
        std::cerr << "Speckley only supports saving via Element functionspaces"
                << std::endl;
        return false;
    }

#ifndef VISIT_PLUGIN
    const pair<int,int> shape = dom->getDataShape(fsType);
    const dim_t* faces = dom->getNumFacesPerBoundary();
    const int* NS = dom->getNumSubdivisionsPerDim();
    const int order = dom->getOrder();

    numElements = shape.second*order*order;
    int nodesPerOrigElement = order*order;
    if (numElements > 0) {
        nodesPerElement = 4;
        if (dom->getDim() == 3) {
            nodesPerElement = 8;
            numElements *= order;
            nodesPerOrigElement *= order;
        }
        owner.assign(numElements, dom->getMPIRank());

        const dim_t* iPtr = dom->borrowSampleReferenceIDs(fsType);
        ID.resize(numElements);
        for (int i = 0; i < shape.second; i++) {
            for (int n = 0; n < nodesPerOrigElement; n++) {
                ID[i*n + n] = iPtr[i];
            }
        }

        const dim_t* NE = dom->getNumElementsPerDim();
        const dim_t* NN = dom->getNumNodesPerDim();
        nodes.clear();
        if (dom->getDim() == 2) {
            type = ZONETYPE_QUAD;
            if (faces[0]==0) {
                owner[0]=(faces[2]==0 ? dom->getMPIRank()-NS[0]-1
                        : dom->getMPIRank()-1);
                for (int i=1; i<NE[1]; i++)
                    owner[i*NE[0]]=dom->getMPIRank()-1;
            }
            if (faces[2]==0) {
                const int first=(faces[0]==0 ? 1 : 0);
                for (int i=first; i<NE[0]; i++)
                    owner[i]=dom->getMPIRank()-NS[0];
            }
            for (int ey = 0; ey < NE[1]; ey++) {
                for (int ex = 0; ex < NE[0]; ex++) {
                    int start = order*(ex + ey*NN[0]);
                    for (int qy=0; qy < order; qy++) {
                        int rowstart = start + qy*NN[0];
                        for (int qx=0; qx < order; qx++) {
                            nodes.push_back(rowstart + qx);
                            nodes.push_back(rowstart + qx + 1);
                            nodes.push_back(rowstart + qx + NN[0] + 1);
                            nodes.push_back(rowstart + qx + NN[0]);
                        }
                    }
                }
            }
        } else {
            type = ZONETYPE_HEX;
            // ownership is not entirely correct but that is not critical.
            // fix when there is time.
            if (faces[1]==0) {
                for (int k2=0; k2<NE[2]; k2++) {
                    for (int k1=0; k1<NE[1]; k1++) {
                        const int e=k2*NE[0]*NE[1]+(k1+1)*NE[0]-1;
                        owner[e]=dom->getMPIRank()+1;
                    }
                }
            }
            if (faces[3]==0) {
                for (int k2=0; k2<NE[2]; k2++) {
                    for (int k0=0; k0<NE[0]; k0++) {
                        const int e=(k2+1)*NE[0]*NE[1]-NE[0]+k0;
                        owner[e]=dom->getMPIRank()+NS[0];
                    }
                }
            }
            if (faces[5]==0) {
                for (int k1=0; k1<NE[1]; k1++) {
                    for (int k0=0; k0<NE[0]; k0++) {
                        const int e=k1*NE[0]+k0+NE[0]*NE[1]*(NE[2]-1);
                        owner[e]=dom->getMPIRank()+NS[0]*NS[1];
                    }
                }
            }
            
            for (int ez = 0; ez < NE[2]; ez++) {
                for (int ey = 0; ey < NE[1]; ey++) {
                    for (int ex = 0; ex < NE[0]; ex++) {
                        int start = order*(ex + ey*NN[0] + ez*NN[0]*NN[1]);
                        for (int qz = 0; qz < order; qz++) {
                            for (int qy=0; qy < order; qy++) {
                                for (int qx=0; qx < order; qx++) {
                                    int xstart = start + qy*NN[0] + qz*NN[0]*NN[1] + qx;
                                    nodes.push_back(xstart);
                                    nodes.push_back(xstart + NN[0]*NN[1]);
                                    nodes.push_back(xstart + NN[0]*NN[1] + 1);
                                    nodes.push_back(xstart + 1);
                                    
                                    nodes.push_back(xstart + NN[0]);
                                    nodes.push_back(xstart + NN[0]*(NN[1]+1));
                                    nodes.push_back(xstart + NN[0]*(NN[1]+1)+1);
                                    nodes.push_back(xstart + NN[0]+1);
                                }
                            }
                        }
                    }
                }
            }
        }

        buildMeshes();
    }
    return true;

#else // VISIT_PLUGIN
    return false;
#endif
}
bool ModelOBJ::import(const char *pszFilename, bool rebuildNormals)
{
    FILE *pFile = fopen(pszFilename, "r");

    if (!pFile)
        return false;

    // Extract the directory the OBJ file is in from the file name.
    // This directory path will be used to load the OBJ's associated MTL file.

    m_directoryPath.clear();

    std::string filename = pszFilename;
    std::string::size_type offset = filename.find_last_of('\\');

    if (offset != std::string::npos)
    {
        m_directoryPath = filename.substr(0, ++offset);
    }
    else
    {
        offset = filename.find_last_of('/');

        if (offset != std::string::npos)
            m_directoryPath = filename.substr(0, ++offset);
    }

    // Import the OBJ file.

    importGeometryFirstPass(pFile);
    rewind(pFile);
    importGeometrySecondPass(pFile);
    fclose(pFile);

    // Perform post import tasks.

    buildMeshes();
    bounds(m_center, m_width, m_height, m_length, m_radius);

    // Build vertex normals if required.

    if (rebuildNormals)
    {
        generateNormals();
    }
    else
    {
        if (!hasNormals())
            generateNormals();
    }

    // Build tangents is required.

    for (int i = 0; i < m_numberOfMaterials; ++i)
    {
        if (!m_materials[i].bumpMapFilename.empty())
        {
            generateTangents();
            break;
        }
    }

    return true;
}