void ArticulatedModel::loadHeightfield(const Specification& specification) {
    Part* part = addPart("root");
    Geometry* geom = addGeometry("geom");
    Mesh* mesh = addMesh("mesh", part, geom);
    mesh->material = UniversalMaterial::create();

    shared_ptr<Image1> im = Image1::fromFile(specification.filename);

    geom->cpuVertexArray.hasTangent = false;
    geom->cpuVertexArray.hasTexCoord0 = true;

    const bool spaceCentered = true;
    const bool generateBackFaces = specification.heightfieldOptions.generateBackfaces;
    const Vector2& textureScale  = specification.heightfieldOptions.textureScale;

    Array<Point3> vertex;
    Array<Point2> texCoord;
    MeshAlg::generateGrid
    (vertex, texCoord, mesh->cpuIndexArray,
     im->width(), im->height(), textureScale,
     spaceCentered,
     generateBackFaces,
     CFrame(Matrix4::scale((float)im->width(), 1.0, (float)im->height()).upper3x3()),
     im);


    // Copy the vertex data into the mesh
    geom->cpuVertexArray.vertex.resize(vertex.size());
    CPUVertexArray::Vertex* vertexPtr = geom->cpuVertexArray.vertex.getCArray();
    for (uint32 i = 0; i < (uint32)vertex.size(); ++i) {
        CPUVertexArray::Vertex& v = vertexPtr[i];
        v.position = vertex[i];
        v.texCoord0 = texCoord[i];
        v.tangent.x = v.normal.x = fnan();
    } // for
}
void ArticulatedModel::loadPLY(const Specification& specification) {

    // Read the data in
    name = FilePath::base(specification.filename);
    Part* part = addPart(name);
    Mesh* mesh = addMesh("mesh", part);
    mesh->material = Material::create();
    
    ParsePLY parseData;
    {
        BinaryInput bi(specification.filename, G3D_LITTLE_ENDIAN);
        parseData.parse(bi);
    }

    // Convert the format
    
    part->cpuVertexArray.vertex.resize(parseData.numVertices);
    part->cpuVertexArray.hasTangent = false;
    part->cpuVertexArray.hasTexCoord0 = false;
    part->m_hasTexCoord0 = false;
     
    // The PLY format is technically completely flexible, so we have
    // to search for the location of the X, Y, and Z fields within each
    // vertex.
    int axisIndex[3];
    const std::string axisName[3] = {"x", "y", "z"};
    const int numVertexProperties = parseData.vertexProperty.size();
    for (int a = 0; a < 3; ++a) {
        axisIndex[a] = 0;
        for (int p = 0; p < numVertexProperties; ++p) {
            if (parseData.vertexProperty[p].name == axisName[a]) {
                axisIndex[a] = p;
                break;
            }
        }
    }

    for (int v = 0; v < parseData.numVertices; ++v) {
        CPUVertexArray::Vertex& vertex = part->cpuVertexArray.vertex[v];

        // Read the position
        for (int a = 0; a < 3; ++a) {
            vertex.position[a] = parseData.vertexData[v * numVertexProperties + axisIndex[a]];
        }

        // Flag the normal as undefined 
        vertex.normal.x = fnan();
    }

    if (parseData.numFaces > 0) {
        // Read faces
        for (int f = 0; f < parseData.numFaces; ++f) {
            const ParsePLY::Face& face = parseData.faceArray[f];
        
            // Read and tessellate into triangles, assuming convex polygons
            for (int i = 2; i < face.size(); ++i) {
                mesh->cpuIndexArray.append(face[0], face[1], face[i]);
            }
        }

    } else {
        // Read tristrips
        for (int f = 0; f < parseData.numTriStrips; ++f) {
            const ParsePLY::TriStrip& triStrip = parseData.triStripArray[f];

            // Convert into an indexed triangle list and append to the end of the 
            // index array.
            bool clockwise = false;
            for (int i = 2; i < triStrip.size(); ++i) {
                if (triStrip[i] == -1) {
                    // Restart
                    clockwise = false;
                    // Skip not only this element, but the next two
                    i += 2;
                } else if (clockwise) {  // clockwise face
                    debugAssert(triStrip[i - 1] >= 0 && triStrip[i - 2] >= 0  && triStrip[i] >= 0);
                    mesh->cpuIndexArray.append(triStrip[i - 1], triStrip[i - 2], triStrip[i]);
                    clockwise = ! clockwise;
                } else { // counter-clockwise face
                    debugAssert(triStrip[i - 1] >= 0 && triStrip[i - 2] >= 0  && triStrip[i] >= 0);
                    mesh->cpuIndexArray.append(triStrip[i - 2], triStrip[i - 1], triStrip[i]);
                    clockwise = ! clockwise;
                }
            }
        }
    }
}
// There is no "ParseOFF" because OFF parsing is trivial--it has no subparts or materials,
// and is directly an indexed format.
void ArticulatedModel::loadOFF(const Specification& specification) {
    
    Part* part      = addPart(m_name);
    Geometry* geom  = addGeometry("geom");
    Mesh* mesh      = addMesh("mesh", part, geom);
    mesh->material = UniversalMaterial::create();
    
    TextInput::Settings s;
    s.cppBlockComments = false;
    s.cppLineComments = false;
    s.otherCommentCharacter = '#';
    TextInput ti(specification.filename, s);           
    
    ///////////////////////////////////////////////////////////////
    // Parse header
    std::string header = ti.readSymbol();
    bool hasTexCoords = false;
    bool hasColors = false;
    bool hasNormals = false;
    bool hasHomogeneous = false;
    bool hasHighDimension = false;

    if (beginsWith(header, "ST")) {
        hasTexCoords = true;
        header = header.substr(2);
    }
    if (beginsWith(header, "C")) {
        hasColors = true;
        header = header.substr(1);
    }
    if (beginsWith(header, "N")) {
        hasNormals = true;
        header = header.substr(1);
    }
    if (beginsWith(header, "4")) {
        hasHomogeneous = true;
        header = header.substr(1);
    }
    if (beginsWith(header, "n")) {
        hasHighDimension = true;
        header = header.substr(1);
    }
    
    geom->cpuVertexArray.hasTexCoord0   = hasTexCoords;
    geom->cpuVertexArray.hasTangent     = false;

    // Remaining header should be "OFF", but is not required according to the spec

    Token t = ti.peek();
    if ((t.type() == Token::SYMBOL) && (t.string() == "BINARY")) {
        throw std::string("BINARY OFF files are not supported by this version of G3D::ArticulatedModel");
    }

    int ndim = 3;
    if (hasHighDimension) {
        ndim = int(ti.readNumber());
    }
    if (hasHomogeneous) {
        ++ndim;
    }

    if (ndim < 3) {
        throw std::string("OFF files must contain at least 3 dimensions");
    }

    int nV = iFloor(ti.readNumber());
    int nF = iFloor(ti.readNumber());
    int nE = iFloor(ti.readNumber());
    (void)nE;

    ///////////////////////////////////////////////////

    Array<int>& index = mesh->cpuIndexArray;

    geom->cpuVertexArray.vertex.resize(nV);
    
    // Read the per-vertex data
    for (int v = 0; v < nV; ++v) {
        CPUVertexArray::Vertex& vertex = geom->cpuVertexArray.vertex[v];

        // Position 
        for (int i = 0; i < 3; ++i) {
            vertex.position[i] = float(ti.readNumber());
        }
        
        // Ignore higher dimensions
        for (int i = 3; i < ndim; ++i) {
            (void)ti.readNumber();
        }

        if (hasNormals) {
            // Normal (assume always 3 components)
            for (int i = 0; i < 3; ++i) {
                vertex.normal[i] = float(ti.readNumber());
            }
        } else {
            vertex.normal.x = fnan();
        }

        if (hasColors) {
            // Color (assume always 3 components)
            for (int i = 0; i < 3; ++i) {
                ti.readNumber();
            }
        }

        if (hasTexCoords) {
            // Texcoords (assume always 2 components)
            for (int i = 0; i < 2; ++i) {
                vertex.texCoord0[i] = float(ti.readNumber());
            }
        }
        // Skip to the end of the line.  If the file was corrupt we'll at least get the next vertex right
        ti.readUntilNewlineAsString();
    }

    // Faces

    // Convert arbitrary triangle fans to triangles
    Array<int> poly;
    for (int i = 0; i < nF; ++i) {
        poly.fastClear();
        int polySize = iFloor(ti.readNumber());
        debugAssert(polySize > 2);

        if (polySize == 3) {
            // Triangle (common case)
            for (int j = 0; j < 3; ++j) {
                index.append(iFloor(ti.readNumber()));
            }
        } else {
            poly.resize(polySize);
            for (int j = 0; j < polySize; ++j) {
                poly[j] = iFloor(ti.readNumber());
                debugAssertM(poly[j] < nV, 
                             "OFF file contained an index greater than the number of vertices."); 
            }

            // Expand the poly into triangles
            MeshAlg::toIndexedTriList(poly, PrimitiveType::TRIANGLE_FAN, index);
        }

        // Trim to the end of the line, except on the last line of the
        // file (where it doesn't matter)
        if (i != nF - 1) {
            // Ignore per-face colors
            ti.readUntilNewlineAsString();
        }
    }
}
Exemple #4
0
bool isNaN(float x) {
    static const float n = fnan();
    return memcmp(&x, &n, sizeof(float)) == 0;
}
// There is no "ParseIFS" because IFS parsing is trivial--it has no subparts or materials,
// and is directly an indexed format.
void ArticulatedModel::loadIFS(const Specification& specification) {
    Part* part          = addPart("root");
    Geometry* geometry  = addGeometry("geom");
    Mesh* mesh          = addMesh("mesh", part, geometry);
    
    mesh->material = UniversalMaterial::create();
    
    BinaryInput bi(specification.filename, G3D_LITTLE_ENDIAN);

    if (bi.getLength() == 0) {
        throw String("Failed to open " + specification.filename);
    }
        
    const String& header = bi.readString32();
    if (strcmp(header.c_str(), "IFS") != 0) {
        throw String("File is not an IFS file");
    }

    const float32 ifsversion  = bi.readFloat32();
    if (ifsversion != 1.0f && ifsversion != 1.1f) {
        throw String("Bad IFS version, expecting 1.0 or 1.1");
    }
        
    m_name = bi.readString32();

    geometry->cpuVertexArray.hasTangent = false;
    geometry->cpuVertexArray.hasTexCoord0 = false;

    while (bi.hasMore()) {
        String str = bi.readString32();
            
        if (str == "VERTICES") {
            debugAssertM(geometry->cpuVertexArray.size() == 0, "Multiple vertex fields!");
            const uint32 num = bi.readUInt32();
                
            if ((num <= 0) || (num > 10000000)) {
                throw String("Bad number of vertices");
            }
            
            geometry->cpuVertexArray.vertex.resize(num);
               
            CPUVertexArray::Vertex* vertexPtr = geometry->cpuVertexArray.vertex.getCArray();
            for (uint32 i = 0; i < num; ++i) {
                CPUVertexArray::Vertex& vertex = vertexPtr[i];
                vertex.position.deserialize(bi);
                vertex.tangent.x = vertex.normal.x = fnan();
            }
                
        } else if (str == "TRIANGLES") {
            debugAssertM(mesh->cpuIndexArray.size() == 0,
                            "Multiple triangle fields!");
            const uint32 num = bi.readUInt32();
                
            if ((num <= 0) || (num > 100000000)) {
                throw String("Bad number of triangles");
            }
                
            mesh->cpuIndexArray.resize(num * 3);
            for (uint32 i = 0; i < (uint32)mesh->cpuIndexArray.size(); ++i) {
                mesh->cpuIndexArray[i] = bi.readUInt32();
            }
        } else if (str == "TEXTURECOORD") {
            debugAssertM(ifsversion == 1.1f,
                            "IFS Version should be 1.1");
            const uint32 num = bi.readUInt32();
            debugAssertM((int)num == geometry->cpuVertexArray.size(),
                            " Must have same number of texcoords as vertices");

            geometry->cpuVertexArray.hasTexCoord0 = true;
            CPUVertexArray::Vertex* vertexPtr = geometry->cpuVertexArray.vertex.getCArray();
            for(uint32 t = 0; t < num; ++t) {
                vertexPtr[t].texCoord0.deserialize(bi);
            }
        }
    } // while has more data

}