Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

vpmeshobject.cpp

Go to the documentation of this file.
00001 
00002 
00003 
00004 
00005 // ChangeLog
00006 // Jun 13, 2005 - Bruno de Oliveira Schneider
00007 // - Moved code to enable vertex arrays from constructor into vpviewerglutogl.
00008 // - Added IsEmpty.
00009 // - DrawInstanceOGL now respects the "show" attribute.
00010 // Jan 31, 2005 - Bruno de Oliveira Schneider
00011 // - Added faceCounter to ReadFromOBJ.
00012 // Dec 16, 2004 - Bruno de Oliveira Schneider
00013 // - Added Clear, MakeBox, GetYProjection, SetMaterial, DrawInstanceOGL and GetID.
00014 // - Attribute vertVec was a vector of pointer to points and is now a vector of
00015 //   points. Work with the ConvexHull class showed that it would be better if a
00016 //   MeshObject could manage its own storage. Accordingly, normVec had its type
00017 //   changed.
00018 // - Fixed DrawInstanceOGL(), so that it now works with unoptimized meshes.
00019 // - Added SetVertices, AddFace and ComputeBoundingBox.
00020 // - Added Transform, MergeWith, ReadFromOBJ and ReadMaterialTable.
00021 // - Added a copy constructor and operator=.
00022 // Sep 22, 2004 - Bruno de Oliveira Schneider 
00023 // - File created.
00024 
00025 #include "vpmeshobject.h"
00026 #include <sstream>
00027 #include <cassert>
00028 #include <fstream>
00029 
00030 using namespace std;
00031 
00032 VPMeshObject::VPMeshObject()
00033 {
00034 }
00035 
00036 VPMeshObject::VPMeshObject(const VPMeshObject& obj)
00037 {
00038     vertVec = obj.vertVec;
00039     vertCoordVec = obj.vertCoordVec;
00040     normVec = obj.normVec;
00041     normCoordVec = obj.normCoordVec;
00042     textCoordVec = obj.textCoordVec;
00043     meshList = obj.meshList;
00044 }
00045 
00046 VPMeshObject& VPMeshObject::operator=(const VPMeshObject& obj)
00047 {
00048     vertVec = obj.vertVec;
00049     vertCoordVec = obj.vertCoordVec;
00050     normVec = obj.normVec;
00051     normCoordVec = obj.normCoordVec;
00052     textCoordVec = obj.textCoordVec;
00053     meshList = obj.meshList;
00054     return *this;
00055 }
00056 
00057 void VPMeshObject::Clear()
00058 {
00059     vertVec.clear();
00060     vertCoordVec.clear();
00061     normVec.clear();
00062     normCoordVec.clear();
00063     textCoordVec.clear();
00064     meshList.clear();
00065 }
00066 
00067 void VPMeshObject::SetMaterial(const VPMaterial& mat)
00068 {
00069     list<VPMesh>::iterator iter;
00070     for (iter = meshList.begin(); iter != meshList.end(); ++iter)
00071         iter->material = mat;
00072 }
00073 
00074 void VPMeshObject::SetVertices(const std::vector<VPPoint4D>& vertexVec)
00075 {
00076     vertVec = vertexVec;
00077     meshList.clear();
00078     ComputeBoundingBox();
00079     ComputeRecursiveBoundingBox();
00080 }
00081 
00082 void VPMeshObject::SetVertices(const char* vertexStr)
00083 {
00084     string valStr = vertexStr;
00085     istringstream iss(valStr);
00086     double x,y,z,w;
00087     bool notFinished = true;
00088     VPPoint4D point;
00089 
00090     vertVec.clear();
00091     do {
00092         if (!(iss >> x >> y >> z)) // Try to read 3 values
00093             notFinished = false; // signal end of parsing
00094         else
00095         {
00096             if (!(iss >> w)) // try to read 4th value
00097             {
00098                 w = 1.0; // use default value
00099                 iss.clear(); // erase error flags
00100             }
00101             point.SetXYZW(x,y,z,w);
00102             vertVec.push_back(point);
00103             iss >> ws; // skip possible white space before comma
00104             iss.get(); // skip the comma
00105         }
00106     } while (notFinished);
00107     meshList.clear();
00108     ComputeBoundingBox();
00109     ComputeRecursiveBoundingBox();
00110 }
00111 
00112 void VPMeshObject::AddFace(const char* indexStr)
00113 {
00114     string valStr = indexStr;
00115     istringstream iss(valStr);
00116     unsigned int value;
00117     unsigned int thisFacesNormalIndex = normVec.size();
00118     VPMesh mesh;
00119 
00120     mesh.type = VPMesh::POLYGON;
00121     while (iss >> value)
00122     {
00123         mesh.indexVec.push_back(value);
00124         mesh.normIndVec.push_back(thisFacesNormalIndex);
00125     }
00126     meshList.push_back(mesh);
00127 
00128     // Auto computation of face normal
00129     // FixMe: It should be possible to disable auto computation
00130     VPPoint4D v1 = vertVec[mesh.indexVec[1]] - vertVec[mesh.indexVec[0]];
00131     VPPoint4D v2 = vertVec[mesh.indexVec[2]] - vertVec[mesh.indexVec[1]];
00132     v1.Normalize();
00133     v2.Normalize();
00134     VPPoint4D normal = v1.CrossProduct(v2);
00135     normVec.push_back(normal);
00136 }
00137 
00138 void VPMeshObject::MakeBox(double minX, double maxX, double minY, double maxY, double minZ, double maxZ)
00139 {
00140     assert((minX <= maxX) && (minY <= maxY) && (minZ <= maxZ));
00141     // each vertex must repeat 3 times because there must be a vertex/normal correspondence
00142     // of 1:1
00143     double coordinateArray[] = { minX,minY,minZ, //0
00144                                  minX,maxY,minZ, //1
00145                                  maxX,maxY,minZ, //2
00146                                  maxX,minY,minZ, //3
00147                                  minX,minY,maxZ, //4
00148                                  minX,maxY,maxZ, //5
00149                                  maxX,maxY,maxZ, //6
00150                                  maxX,minY,maxZ, //7
00151                                  minX,minY,minZ,
00152                                  minX,maxY,minZ,
00153                                  maxX,maxY,minZ,
00154                                  maxX,minY,minZ,
00155                                  minX,minY,maxZ,
00156                                  minX,maxY,maxZ,
00157                                  maxX,maxY,maxZ,
00158                                  maxX,minY,maxZ,
00159                                  minX,minY,minZ,
00160                                  minX,maxY,minZ,
00161                                  maxX,maxY,minZ,
00162                                  maxX,minY,minZ,
00163                                  minX,minY,maxZ,
00164                                  minX,maxY,maxZ,
00165                                  maxX,maxY,maxZ,
00166                                  maxX,minY,maxZ };
00167     double* endOfCoordinateArray = coordinateArray + sizeof(coordinateArray)/sizeof(double);
00168     unsigned int indexArray[] = { 0,1,2,3, // back face
00169                                   4,7,6,5, // front face
00170                                   11,10,14,15, // right face
00171                                   13,9,8,12, // left face
00172                                   22,18,17,21, // top face
00173                                   23,20,16,19 }; // bottom face
00174     double normalArray[] = { 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1,
00175                              0,0,1, 0,0,1, 0,0,1, 0,0,1,
00176                              -1,0,0, -1,0,0, 1,0,0, 1,0,0,
00177                              -1,0,0, -1,0,0, 1,0,0, 1,0,0,
00178                              0,-1,0, 0,1,0, 0,1,0, 0,-1,0,
00179                              0,-1,0, 0,1,0, 0,1,0, 0,-1,0 };
00180     double* endOfNormalArray = normalArray + sizeof(normalArray)/sizeof(double);
00181     unsigned int* endOfIndexArray = indexArray + sizeof(indexArray)/sizeof(int);
00182     VPMesh mesh;
00183 
00184     normCoordVec.clear();
00185     textCoordVec.clear();
00186     meshList.clear();
00187     vertCoordVec.assign(coordinateArray,endOfCoordinateArray);
00188     normCoordVec.assign(normalArray,endOfNormalArray);
00189     mesh.type = VPMesh::QUADS;
00190     mesh.indexVec.assign(indexArray,endOfIndexArray);
00191     mesh.material = VPMaterial::DARK_PLASTIC_GRAY(); // default material
00192     meshList.push_back(mesh);
00193     ComputeBoundingBox();
00194     ComputeRecursiveBoundingBox();
00195 }
00196 
00197 void VPMeshObject::GetYProjection(std::list<VPPoint4D>* resultPtr, double height) const
00198 // height defaults to 0 (see headers).
00199 {
00200     VPPoint4D point;
00201     resultPtr->clear();
00202     if (vertCoordVec.size() > 6)
00203     { // Found optimized data - use it
00204         unsigned int vertexVetEnd = vertCoordVec.size();
00205         for (unsigned int i=0; i < vertexVetEnd; i+=3)
00206         {
00207             point.SetXYZW(vertCoordVec[i],height,vertCoordVec[i+2],1);
00208             resultPtr->push_back(point);
00209         }
00210     }
00211     else
00212     { // Use unoptimized data
00213         unsigned int vertexVetEnd = vertVec.size();
00214         for (unsigned int i=0; i < vertexVetEnd; ++i)
00215         {
00216             point.SetXYZW(vertVec[i].GetX(), height, vertVec[i].GetZ(), 1);
00217             resultPtr->push_back(point);
00218         }
00219     }
00220 }
00221 
00222 void VPMeshObject::Optimize()
00223 {
00224     // Create optmized structures from unoptimized ones
00225     // Erase unoptimized data
00226 }
00227 
00228 void VPMeshObject::ComputeBoundingBox() {
00229     if (vertCoordVec.size() > 0)
00230     { // Optimized structure found - use it!
00231         // Initialize
00232         bBox.SetBoundingBox(vertCoordVec[0], vertCoordVec[1], vertCoordVec[2],
00233                             vertCoordVec[0], vertCoordVec[1], vertCoordVec[2]);
00234         // Check against the others
00235         for (unsigned int i=3; i < vertCoordVec.size(); i+=3)
00236             bBox.ConditionalUpdate(vertCoordVec[i], vertCoordVec[i+1], vertCoordVec[i+2]);
00237     }
00238     else
00239     { // No optmized structure found - use vertVec
00240         // Initialize
00241         bBox.SetBoundingBox(vertVec[0].GetX(), vertVec[0].GetY(), vertVec[0].GetZ(),
00242                             vertVec[0].GetX(), vertVec[0].GetY(), vertVec[0].GetZ());
00243         // Check against the others
00244         for (unsigned int i=1; i < vertVec.size(); ++i)
00245             bBox.ConditionalUpdate(vertVec[i]);
00246     }
00247 }
00248 
00249 void VPMeshObject::MergeWith(const VPMeshObject& obj) {
00250 // both meshObjects must be optimized or the both must be unoptimized
00251     bool bothOptimized = vertVec.empty() && obj.vertVec.empty();
00252     list<VPMesh>::const_iterator iter = obj.meshList.begin();
00253     VPMesh mesh;
00254     unsigned int prevNumVertices;
00255     assert (bothOptimized || (vertCoordVec.empty() && obj.vertCoordVec.empty()));
00256 
00257     prevNumVertices = (bothOptimized? (vertCoordVec.size()/3) : vertVec.size());
00258     for (; iter != obj.meshList.end(); ++iter)
00259     {
00260         mesh = *iter;
00261         mesh.IncrementIndices(prevNumVertices);
00262         meshList.push_back(mesh);
00263     }
00264 
00265     vertVec.insert(vertVec.end(), obj.vertVec.begin(), obj.vertVec.end());
00266     vertCoordVec.insert(vertCoordVec.end(), obj.vertCoordVec.begin(), obj.vertCoordVec.end());
00267     normVec.insert(normVec.end(), obj.normVec.begin(), obj.normVec.end());
00268     normCoordVec.insert(normCoordVec.end(), obj.normCoordVec.begin(), obj.normCoordVec.end());
00269     textCoordVec.insert(textCoordVec.end(), obj.textCoordVec.begin(), obj.textCoordVec.end());
00270     ComputeBoundingBox();
00271     ComputeRecursiveBoundingBox();
00272 }
00273 
00274 void VPMeshObject::Transform(const VPTransform& trans) {
00275     unsigned int i = 0;
00276     unsigned int size;
00277 
00278     if (vertCoordVec.empty())
00279     {
00280         for (size = vertVec.size(); i < size; ++i)
00281         {
00282             trans.ApplyTo(&(vertVec[i]));
00283         }
00284     }
00285     else
00286     {
00287         VPPoint4D vertex;
00288         for (size = vertCoordVec.size(); i < size; i+=3)
00289         {
00290             vertex.SetXYZW(vertCoordVec[i],vertCoordVec[i+1],vertCoordVec[i+2],1);
00291             trans.ApplyTo(&vertex);
00292             vertCoordVec[i] = vertex.GetX();
00293             vertCoordVec[i+1] = vertex.GetY();
00294             vertCoordVec[i+2] = vertex.GetZ();
00295         }
00296     }
00297     ComputeBoundingBox();
00298     ComputeRecursiveBoundingBox();
00299 }
00300 
00301 void VPMeshObject::ComputeTriangleNormal(const VPPoint4D& v1, const VPPoint4D& v2,
00302                                          const VPPoint4D& v3, VPPoint4D* resultPtr) {
00303     VPPoint4D edge1 = v2 - v1;
00304     VPPoint4D edge2 = v3 - v2;
00305     // FixMe: Tent normals seem to be inverted (note minus operator below)
00306     // investigate and solve...
00307     *resultPtr = -edge1.CrossProduct(edge2);
00308     resultPtr->Normalize();
00309 }
00310 
00311 bool VPMeshObject::DrawInstanceOGL() const {
00312 // Note that vertex arrays must be enabled to allow drawing of optimized meshes. See
00313 // VPViewerGlutOGL.
00314 #ifdef VP_OGL
00315     bool result = true;
00316     list<VPMesh>::const_iterator iter;
00317     if (show) // if visible...
00318     {         // FixMe: no need to keep this old name; rename "show" to "visible".
00319         if (vertCoordVec.size() > 0)
00320         { // Optimized structure found - draw it!
00321             glVertexPointer(3, GL_DOUBLE, 0, &vertCoordVec[0]);
00322             glNormalPointer(GL_DOUBLE, 0, &normCoordVec[0]);
00323             assert(vertCoordVec.size() == normCoordVec.size());
00324             for (iter = meshList.begin(); iter != meshList.end(); ++iter)
00325             { // for each mesh:
00326                 result &= iter->DrawInstanceOGL();
00327             }
00328         }
00329         else
00330         { // No optmized structure found - draw vertices from vertVec
00331             unsigned int meshSize;
00332             unsigned int i;
00333             for (iter = meshList.begin(); iter != meshList.end(); ++iter)
00334             { // for each mesh:
00335                 iter->material.DrawOGL();
00336                 glBegin(iter->GetOglType());
00337                 meshSize = iter->indexVec.size();
00338                 assert(meshSize == iter->normIndVec.size());
00339                 for (i = 0; i < meshSize; ++i)
00340                 {
00341                     glNormal3dv(normVec[iter->normIndVec[i]].VetXYZW());
00342                     glVertex4dv(vertVec[iter->indexVec[i]].VetXYZW());
00343                 }
00344                 glEnd();
00345             }
00346         }
00347     }
00348     return result;
00349 #else
00350     return false;
00351 #endif
00352 }
00353 
00354 void VPMeshObject::ReadFromOBJ(const string& filename, list<VPMeshObject*>* resultPtr)
00355 // passing garbage on *resultPtr makes the method crash. Remember to clean it before calling.
00356 // FixMe: This method currently handles only triangle meshes!
00357 {
00358     ifstream file(filename.c_str());
00359     istringstream iss;
00360     string line;
00361     string lineID;
00362     string name;
00363     string materialName;
00364     double x,y,z;
00365     map<string,VPMaterial> materialMap;
00366     vector<double> vertCoordTempVec;
00367     VPMeshObject* meshObjectPtr = NULL;
00368     VPMesh mesh;
00369     char slash;
00370     bool noVertexMemoryAllocated = true;
00371     unsigned int vi, ti, ni; // vertex, texture and normal indices
00372     unsigned int lastNormIndex = 1;
00373     unsigned int lastVertIndex = 1;
00374     unsigned int coordIndex, tempIndex;
00375     unsigned int faceCounter = 0; // counts the number of faces in the file
00376 
00377     cout << "Loading " << filename << "..." << flush;
00378     mesh.type = VPMesh::TRIANGLES;
00379     while (getline(file,line))
00380     {
00381         iss.clear();
00382         iss.str(line); // iss <- line
00383         iss >> lineID;
00384         if (lineID == "v") // vertex
00385         {
00386             iss >> x >> y >> z;
00387             vertCoordTempVec.push_back(x);
00388             vertCoordTempVec.push_back(y);
00389             vertCoordTempVec.push_back(z);
00390         }
00391         else if (lineID == "vn") // vertex normal
00392         {
00393             iss >> x >> y >> z;
00394             meshObjectPtr->normCoordVec.push_back(x);
00395             meshObjectPtr->normCoordVec.push_back(y);
00396             meshObjectPtr->normCoordVec.push_back(z);
00397         }
00398         else if (lineID == "f") // face
00399         {
00400             if (noVertexMemoryAllocated)
00401             {
00402                 noVertexMemoryAllocated = false;
00403                 meshObjectPtr->vertCoordVec.assign(meshObjectPtr->normCoordVec.size(),0);
00404             }
00405             while (iss >> vi >> slash >> ti >> slash >> ni)
00406             {
00407                 coordIndex = 3 * (ni - lastNormIndex);
00408                 tempIndex = 3 * (vi - lastVertIndex);
00409                 meshObjectPtr->vertCoordVec[coordIndex++] = vertCoordTempVec[tempIndex];
00410                 ++tempIndex;
00411                 meshObjectPtr->vertCoordVec[coordIndex++] = vertCoordTempVec[tempIndex];
00412                 ++tempIndex;
00413                 meshObjectPtr->vertCoordVec[coordIndex]   = vertCoordTempVec[tempIndex];
00414                 mesh.indexVec.push_back(ni - lastNormIndex);
00415             }
00416             ++faceCounter;
00417         }
00418         else if (lineID == "vt") // texture coordinates
00419         {
00420         }
00421         else if (lineID == "mtllib") // material library
00422         {
00423             iss >> ws >> name;
00424             ReadMaterialTable(name, &materialMap);
00425         }
00426         else if (lineID == "o") // object delimiter
00427         {
00428             // Add last mesh to last meshObject
00429             if (mesh.indexVec.size() > 0)
00430             {
00431                 meshObjectPtr->meshList.push_back(mesh);
00432                 lastNormIndex += (meshObjectPtr->normCoordVec.size()/3);
00433                 lastVertIndex += (vertCoordTempVec.size()/3);
00434                 mesh.indexVec.clear();
00435                 vertCoordTempVec.clear(); // it seems that clear() does not free memory
00436                                           // therefore this should not slow down the method.
00437                 noVertexMemoryAllocated = true;
00438             }
00439 
00440             iss >> ws >> name;
00441             meshObjectPtr = new VPMeshObject;
00442             meshObjectPtr->autoDelete = true;
00443             meshObjectPtr->SetDescription(name);
00444             resultPtr->push_back(meshObjectPtr);
00445         }
00446         else if (lineID == "usemtl") // material assignment
00447         { // start new mesh
00448             // add old mesh to meshObject
00449             if (mesh.indexVec.size() > 0)
00450             {
00451                 meshObjectPtr->meshList.push_back(mesh);
00452                 mesh.indexVec.clear();
00453                 mesh.normIndVec.clear();
00454             }
00455 
00456             iss >> ws >> materialName;
00457             mesh.material = materialMap[materialName];
00458         }
00459         else if (lineID == "#")
00460         { // ignore this line (comment)
00461         }
00462         else
00463             cerr << "Error: unknown line ID: '" << lineID << "'" << endl;
00464     }
00465     // Finished. Add last mesh to last meshObject
00466     if (mesh.indexVec.size() > 0)
00467     {
00468         meshObjectPtr->meshList.push_back(mesh);
00469     }
00470     // Compute bounding boxes
00471     list<VPMeshObject*>::iterator iter;
00472     for (iter = resultPtr->begin(); iter != resultPtr->end(); ++iter)
00473     {
00474         (*iter)->ComputeBoundingBox();
00475         (*iter)->ComputeRecursiveBoundingBox();
00476     }
00477     cout << " (" << faceCounter << " polygons)" << endl; // for "loading filename..."
00478 }
00479 
00480 //~ void VPMeshObject::YSplit(double y, VPMeshObject* ptObjBelow, VPMeshObject* ptObjAbove) const
00481 //~ {
00482     //~ list<VPMesh>::iterator iter;
00483     //~ for (iter = meshList.begin(); iter != meshList.end(); ++iter)
00484     //~ {
00485         //~ switch (iter->type)
00486         //~ { // FixMe: implement more mesh types!
00487             //~ case POINTS:
00488                 //~ break;
00489             //~ case LINES:
00490                 //~ break;
00491             //~ case LINE_STRIP:
00492                 //~ break;
00493             //~ case LINE_LOOP:
00494                 //~ break;
00495             //~ case TRIANGLES:
00496                 //~ break;
00497             //~ case TRIANGLE_STRIP:
00498                 //~ break;
00499             //~ case TRIANGLE_FAN:
00500                 //~ break;
00501             //~ case QUADS:
00502                 //~ break;
00503             //~ case QUAD_STRIP:
00504                 //~ break;
00505             //~ case POLYGON:
00506                 //~ break;
00507             //~ default:
00508                 //~ assert (0 && "VPMeshObject::YSplit: mesh type not implemented!");
00509         //~ }
00510     //~ }
00511 //~ }
00512 
00513 void VPMeshObject::ReadMaterialTable(const string& filename, map<string,VPMaterial>* matMapPtr)
00514 // Reads a Wavefront material table (.mtl file)
00515 {
00516     ifstream file(filename.c_str());
00517     istringstream iss;
00518     string line;
00519     string lineID;
00520     string materialName;
00521     VPMaterial material;
00522     float r,g,b;
00523     float value;
00524     unsigned int type;
00525 
00526     material.SetEmissiveColor(VPColor::BLACK());
00527     while (getline(file,line))
00528     {
00529         iss.str(line); // iss <- line
00530         iss >> lineID;
00531         if (lineID == "newmtl")
00532         {
00533             if (materialName.size() > 0)
00534                 matMapPtr->insert(make_pair(materialName,material));
00535             iss >> ws >> materialName;
00536         }
00537         else if (lineID == "Ns")
00538         {
00539             iss >> value;
00540             // shininess values appear to be in the range 0..1000, so they should be
00541             // multiplied by 0.128
00542             material.SetShininess(value*0.128);
00543         }
00544         else if (lineID == "Kd")
00545         {
00546             iss >> r >> g >> b;
00547             material.SetDiffuseColor(VPColor(static_cast<unsigned char>(r*255),
00548                                              static_cast<unsigned char>(g*255),
00549                                              static_cast<unsigned char>(b*255)));
00550         }
00551         else if (lineID == "Ka")
00552         {
00553             iss >> r >> g >> b;
00554             material.SetAmbientColor(VPColor(static_cast<unsigned char>(r*255),
00555                                              static_cast<unsigned char>(g*255),
00556                                              static_cast<unsigned char>(b*255)));
00557         }
00558         else if (lineID == "Ks")
00559         {
00560             iss >> r >> g >> b;
00561             material.SetSpecularColor(VPColor(static_cast<unsigned char>(r*255),
00562                                               static_cast<unsigned char>(g*255),
00563                                               static_cast<unsigned char>(b*255)));
00564         }
00565         else if ((lineID == "d") || (lineID == "Tr"))
00566         {
00567             iss >> value; // transparency value
00568             // FixMe: use the value
00569         }
00570         else if (lineID == "illum")
00571         {
00572             iss >> type;
00573             if (type == 1) // no specular color for this material
00574                 material.SetSpecularColor(VPColor::BLACK());
00575                 //~ material.SetSpecularColor(material.GetDiffuseColor());
00576         }
00577         else
00578         {
00579             cerr << "Error: unknown line ID: '" << lineID << "'" << endl;
00580         }
00581         iss.clear();
00582     }
00583     // Add last material
00584     if (materialName.size() > 0)
00585         matMapPtr->insert(make_pair(materialName,material));
00586 }

Generated on Tue Sep 6 10:00:04 2005 for VPAT by  doxygen 1.4.4