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(); } } }
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 }