// --------------------------------------------------------------------------- // Includes for all the program files to see // --------------------------------------------------------------------------- #include #include #include using namespace std; #include // --------------------------------------------------------------------------- // Includes // --------------------------------------------------------------------------- #include #include #include #include // --------------------------------------------------------------------------- // Includes // --------------------------------------------------------------------------- #include #include #include #include "X3dParser.h" #include "X3dParserXerces.h" #include "Mesh.h" #include "SceneGraph.h" #include "Triangle3.h" #include "ViewCellsManager.h" #include "ResourceManager.h" #include "IntersectableWrapper.h" #include namespace GtpVisibilityPreprocessor { float X3dParser::DEFAULT_VIEWCELL_HEIGHT = 5.0f; // --------------------------------------------------------------------------- // Local data // // doNamespaces // Indicates whether namespace processing should be enabled or not. // The default is no, but -n overrides that. // // doSchema // Indicates whether schema processing should be enabled or not. // The default is no, but -s overrides that. // // schemaFullChecking // Indicates whether full schema constraint checking should be enabled or not. // The default is no, but -s overrides that. // // valScheme // Indicates what validation scheme to use. It defaults to 'auto', but // can be set via the -v= command. // --------------------------------------------------------------------------- static bool doNamespaces = false; static bool doSchema = false; static bool schemaFullChecking = false; static SAXParser::ValSchemes valScheme = SAXParser::Val_Auto; #define ROTATE_SCENE 0 static int sUniqueMeshIdx = 0; // hack: rotate mesh by n degrees static void RotateMesh(Mesh *mesh, const float angle) { VertexContainer::iterator it, it_end = mesh->mVertices.end(); const float angleRad = angle * PI / 180.0f; const Matrix4x4 rot = RotationYMatrix(angleRad); for (it = mesh->mVertices.begin(); it != it_end; ++ it) { (*it) = rot * (*it); } } // --------------------------------------------------------------------------- // StdInParseHandlers: Constructors and Destructor // --------------------------------------------------------------------------- X3dParseHandlers::X3dParseHandlers(SceneGraphNode *root, const bool loadMeshes): mElementCount(0) , mAttrCount(0) , mCharacterCount(0) , mSpaceCount(0) , mLoadMeshes(loadMeshes) , mCurrentMesh(NULL) { mCurrentNode = root; // this matrix should never be removed from stack //mTransformations.push(IdentityMatrix()); } X3dParseHandlers::~X3dParseHandlers() { assert(mTransformations.empty()); if (0 && !mTransformations.empty()) cout << "error: transformation stack size: " << (int)mTransformations.size() << endl; } // --------------------------------------------------------------------------- // StdInParseHandlers: Implementation of the SAX DocumentHandler interface // --------------------------------------------------------------------------- void X3dParseHandlers::endElement(const XMLCh* const name) { StrX lname(name); string element(lname.LocalForm()); // only create new mesh instance if define mechanism was not used if (element == "Shape") EndShape(); if (element == "Transform") EndTransform(); } void X3dParseHandlers::ApplyTransformations(TrafoStack trafos, Mesh *mesh) const { while (!trafos.empty()) { const Matrix4x4 m = trafos.top(); trafos.pop(); mesh->ApplyTransformation(m); } } void X3dParseHandlers::ApplyTransformations(TrafoStack trafos, TransformedMeshInstance *mi) const { while (!trafos.empty()) { const Matrix4x4 m = trafos.top(); trafos.pop(); mi->ApplyWorldTransform(m); } } void X3dParseHandlers::StartTransform(AttributeList& attributes) { Matrix4x4 currentTransform = IdentityMatrix(); const int len = attributes.getLength(); Matrix4x4 *rotm = NULL; Matrix4x4 *scalem = NULL; Matrix4x4 *translm = NULL; for (int i = 0; i < len; ++ i) { string attrName(StrX(attributes.getName(i)).LocalForm()); StrX attrValue(attributes.getValue(i)); const char *ptr = attrValue.LocalForm(); if (attrName == "rotation") { Vector3 axis; float angle; if (sscanf(ptr, "%f %f %f %f", &axis.x, &axis.y, &axis.z, &angle) == 4) { rotm = new Matrix4x4(RotationAxisMatrix(axis, angle)); } } else if (attrName == "translation") { Vector3 transl; if (sscanf(ptr, "%f %f %f", &transl.x, &transl.y, &transl.z) == 3) { translm = new Matrix4x4(TranslationMatrix(transl)); } } else if (attrName == "scale") { Vector3 scale; if (sscanf(ptr, "%f %f %f", &scale.x, &scale.y, &scale.z) == 3) { scalem = new Matrix4x4(ScaleMatrix(scale.x, scale.y, scale.z)); } } // todo: scale orientation } if (scalem) currentTransform *= (*scalem); if (rotm) currentTransform *= (*rotm); if (translm) currentTransform *= (*translm); DEL_PTR(scalem); DEL_PTR(rotm); DEL_PTR(translm); mTransformations.push(currentTransform); } void X3dParseHandlers::EndTransform() { mTransformations.pop(); } void X3dParseHandlers::EndShape() { ////////////// //-- shape is only a definition => //-- don't create particular mesh instance if (!mCurrentMesh || mIsMeshDefinition) { return; } if (!mLoadMeshes) { //////////////////////////////////// //-- load data as single triangles instead of whole meshes //cout << "m"; Mesh tempMesh(*mCurrentMesh); ApplyTransformations(mTransformations, &tempMesh); FaceContainer::const_iterator fit, fit_end = tempMesh.mFaces.end(); for (fit = tempMesh.mFaces.begin(); fit != fit_end; ++ fit) { //cout << "f"; // triangulate the faces Face *face = *fit; vector triangles; Polygon3 poly(face, &tempMesh); poly.Triangulate(triangles); vector::const_iterator tit, tit_end = triangles.end(); for (tit = triangles.begin(); tit != tit_end; ++ tit) { //cout << "triangle: " << *tit << endl; TriangleIntersectable *ti = new TriangleIntersectable(*tit); mCurrentNode->mGeometry.push_back(ti); } #if 0 // we create a new mesh for each face from the current mesh Mesh *mesh = MeshManager::GetSingleton()->CreateResource(); VertexIndexContainer::const_iterator vit, vit_end = face->mVertexIndices.end(); int i = 0; // dummy vertex indices container VertexIndexContainer vcIndices; for (vit = face->mVertexIndices.begin(); vit != vit_end; ++ vit, ++ i) { cout << "i"; const int index = (*vit); // add vertices mesh->mVertices.push_back(mCurrentMesh->mVertices[index]); // indices don't make much sense if mesh == face, but we need them anyway ... vcIndices.push_back(i); } mesh->mFaces.push_back(new Face(vcIndices)); // write transformations directly into the mesh // note: could be transformed in parent mesh, save some transformations ApplyTransformations(mTransformations, mesh); mesh->Preprocess(); if (mesh->mFaces.empty()) { cout << "error: empy mesh" << endl; } else { // make an instance of this mesh MeshInstance *mi = new MeshInstance(mesh); mCurrentNode->mGeometry.push_back(mi); if (mCurrentMaterial && !mCurrentMesh->mMaterial) { // HACK: add the material to the mesh directly if no material yet mCurrentMesh->mMaterial = mCurrentMaterial; } } #endif } // this mesh is not needed, unless it is used as a definition if (!mUsingMeshDefinition) { MeshManager::GetSingleton()->DestroyEntry(mCurrentMesh->GetId()); } } else // default usage: create a mesh instance from the current mesh { MeshInstance *mi; if (!mUsingMeshDefinition) { // make an instance of this mesh mi = new MeshInstance(mCurrentMesh); // this mesh is used only once => write transformations directly into it ApplyTransformations(mTransformations, mCurrentMesh); } else { // make an instance of this mesh TransformedMeshInstance *tmi = new TransformedMeshInstance(mCurrentMesh); // apply transformation on the instance of the mesh ApplyTransformations(mTransformations, tmi); mi = tmi; } if (mCurrentMaterial) { // HACK: add the material to the mesh directly if no material yet if (!mCurrentMesh->mMaterial) { mCurrentMesh->mMaterial = mCurrentMaterial; } else // add material to the instance { mi->SetMaterial(mCurrentMaterial); } } // create local mesh kd tree mCurrentMesh->Preprocess(); if (mCurrentMesh->mFaces.empty()) { cout << "warning: empy mesh!!" << endl; delete mi; } else { // add to scene graph mCurrentNode->mGeometry.push_back(mi); } // reset current mesh mCurrentMesh = NULL; } } void X3dParseHandlers::StartIndexedFaceSet(AttributeList& attributes) { //-- indexedfaceset corresponds to Mesh in our implementation const int len = attributes.getLength(); VertexIndexContainer vertices; mIsMeshDefinition = false; mUsingMeshDefinition = false; for (int i = 0; i < len; ++ i) { const string attrName(StrX(attributes.getName(i)).LocalForm()); //-- we use an already defined mesh if (attrName == "USE") { StrX attrValue(attributes.getValue(i)); const char *meshName = attrValue.LocalForm(); mUsingMeshDefinition = true; // retrieve mesh from mesh container const int meshIdx = mMeshDefinitions[meshName]; mCurrentMesh = MeshManager::GetSingleton()->FindEntry(meshIdx); //Debug << "retrieving mesh definition: " << mCurrentMeshName << endl; cout << "u"; } else if (attrName == "DEF") //-- a definition of a mesh { const StrX attrValue(attributes.getValue(i)); const char *meshName = attrValue.LocalForm(); // this is only a definition, don't create actual instance mIsMeshDefinition = true; //-- create new mesh definition mCurrentMesh = MeshManager::GetSingleton()->CreateResource(); // store the mesh defination in a lookup table mMeshDefinitions[meshName] = mCurrentMesh->GetId(); cout << "d"; } //-- read coordinate indices for current mesh else if (attrName == "coordIndex") { StrX attrValue(attributes.getValue(i)); const char *ptr = attrValue.LocalForm(); //-- immediate use: create a new mesh using a generic name if (!mCurrentMesh) { mCurrentMesh = MeshManager::GetSingleton()->CreateResource(); } // handle coordIndex vertices.clear(); char *endptr; while (1) { int index = strtol(ptr, &endptr, 10); if (ptr == endptr || index == -1) { if (vertices.size() > 2) { Face *face = new Face(vertices); mCurrentMesh->mFaces.push_back(face); } vertices.clear(); if (ptr == endptr) break; } else { vertices.push_back(index); } ptr = endptr; } } } } void X3dParseHandlers::StartMaterial(AttributeList& attributes) { const int len = attributes.getLength(); mCurrentMaterial = MaterialManager::GetSingleton()->CreateResource(); for (int i = 0; i < len; ++ i) { const string attrName(StrX(attributes.getName(i)).LocalForm()); const StrX attrValue(attributes.getValue(i)); const char *ptr = attrValue.LocalForm(); //-- we use an already defined material if (attrName == "USE") { //mUsingMaterialDefinition = true; string matName(ptr); // retrieve mesh from mesh container const int matIdx = mMaterialDefinitions[matName]; mCurrentMaterial = MaterialManager::GetSingleton()->FindEntry(matIdx); //Debug << "retrieving mesh definition: " << mCurrentMeshName << endl; cout << "u"; } else if (attrName == "DEF") //-- a definition of a material { //mIsMaterialDefinition = true; string matName(ptr); //-- create new material definition mCurrentMaterial = MaterialManager::GetSingleton()->CreateResource(); // store the mesh defination in a lookup table mMaterialDefinitions[matName] = mCurrentMaterial->GetId(); cout << "d"; } // TODO: support not only diffuse material else if (attrName == "diffuseColor") { float r, g, b; if (sscanf(ptr, "%f %f %f", &r, &g, &b) == 3) mCurrentMaterial->mDiffuseColor = RgbColor(r, g, b); } } } void X3dParseHandlers::StartCoordinate(AttributeList& attributes) { const int len = attributes.getLength(); int i; VertexContainer vertices; for (i=0; i < len; i++) { const string attrName(StrX(attributes.getName(i)).LocalForm()); if (attrName == "point") { StrX attrValue(attributes.getValue(i)); const char *ptr = attrValue.LocalForm(); char *endptr; while (1) { float x = (float)strtod(ptr, &endptr); if (ptr == endptr) break; ptr = endptr; float y = (float)strtod(ptr, &endptr); if (ptr == endptr) break; ptr = endptr; float z = (float)strtod(ptr, &endptr); if (ptr == endptr) break; ptr = endptr; if (*ptr == ',') ptr ++; Vector3 v(x, y, z); vertices.push_back(v); } // substitute vertices into current mesh mCurrentMesh->mVertices = vertices; } } } void X3dParseHandlers::startElement(const XMLCh* const name, AttributeList& attributes) { StrX lname(name); string element(lname.LocalForm()); if (element == "IndexedFaceSet") { // create a new mesh node in the scene graph StartIndexedFaceSet(attributes); } if (element == "Shape") { //cout << "+"; // reset current shape values mCurrentMesh = NULL; mCurrentMaterial = NULL; mCurrentVertexIndices.clear(); //mCurrentVertices.clear(); } if (element == "Coordinate") { StartCoordinate(attributes); } // todo if (element == "Material") { StartMaterial(attributes); } if (element == "Transform") { StartTransform(attributes); } ++ mElementCount; mAttrCount += attributes.getLength(); } void X3dParseHandlers::characters(const XMLCh* const chars, const unsigned int length) { mCharacterCount += length; } void X3dParseHandlers::ignorableWhitespace(const XMLCh* const chars, const unsigned int length) { mSpaceCount += length; } void X3dParseHandlers::resetDocument() { mAttrCount = 0; mCharacterCount = 0; mElementCount = 0; mSpaceCount = 0; } // --------------------------------------------------------------------------- // StdInParseHandlers: Overrides of the SAX ErrorHandler interface // --------------------------------------------------------------------------- void X3dParseHandlers::error(const SAXParseException& e) { XERCES_STD_QUALIFIER cerr << "\nError at (file " << StrX(e.getSystemId()) << ", line " << e.getLineNumber() << ", char " << e.getColumnNumber() << "): " << StrX(e.getMessage()) << XERCES_STD_QUALIFIER endl; } void X3dParseHandlers::fatalError(const SAXParseException& e) { XERCES_STD_QUALIFIER cerr << "\nFatal Error at (file " << StrX(e.getSystemId()) << ", line " << e.getLineNumber() << ", char " << e.getColumnNumber() << "): " << StrX(e.getMessage()) << XERCES_STD_QUALIFIER endl; } void X3dParseHandlers::warning(const SAXParseException& e) { XERCES_STD_QUALIFIER cerr << "\nWarning at (file " << StrX(e.getSystemId()) << ", line " << e.getLineNumber() << ", char " << e.getColumnNumber() << "): " << StrX(e.getMessage()) << XERCES_STD_QUALIFIER endl; } /*************************************************************************/ /* X3dParser implementation */ /*******************+*****************************************************/ X3dParser::X3dParser(): mViewCellHeight(DEFAULT_VIEWCELL_HEIGHT) {} bool X3dParser::ParseFile(const string filename, SceneGraphNode *root, const bool loadMeshes, vector *parents) { // Initialize the XML4C system try { XMLPlatformUtils::Initialize(); } catch (const XMLException& toCatch) { XERCES_STD_QUALIFIER cerr << "Error during initialization! Message:\n" << StrX(toCatch.getMessage()) << XERCES_STD_QUALIFIER endl; return false; } // // Create a SAX parser object. Then, according to what we were told on // the command line, set the options. // SAXParser* parser = new SAXParser; parser->setValidationScheme(valScheme); parser->setDoNamespaces(doNamespaces); parser->setDoSchema(doSchema); parser->setValidationSchemaFullChecking(schemaFullChecking); // // Create our SAX handler object and install it on the parser, as the // document and error handler. We are responsible for cleaning them // up, but since its just stack based here, there's nothing special // to do. // X3dParseHandlers handler(root, loadMeshes); parser->setDocumentHandler(&handler); parser->setErrorHandler(&handler); unsigned long duration; int errorCount = 0; // create a faux scope so that 'src' destructor is called before // XMLPlatformUtils::Terminate { // // Kick off the parse and catch any exceptions. Create a standard // input input source and tell the parser to parse from that. // // StdInInputSource src; try { const unsigned long startMillis = XMLPlatformUtils::getCurrentMillis(); parser->parse(filename.c_str()); const unsigned long endMillis = XMLPlatformUtils::getCurrentMillis(); duration = endMillis - startMillis; errorCount = parser->getErrorCount(); } catch (const OutOfMemoryException&) { XERCES_STD_QUALIFIER cerr << "OutOfMemoryException" << XERCES_STD_QUALIFIER endl; errorCount = 2; return false; } catch (const XMLException& e) { XERCES_STD_QUALIFIER cerr << "\nError during parsing: \n" << StrX(e.getMessage()) << "\n" << XERCES_STD_QUALIFIER endl; errorCount = 1; return false; } // Print out the stats that we collected and time taken if (!errorCount) { XERCES_STD_QUALIFIER cout << filename << ": " << duration << " ms (" << handler.GetElementCount() << " elems, " << handler.GetAttrCount() << " attrs, " << handler.GetSpaceCount() << " spaces, " << handler.GetCharacterCount() << " chars)" << XERCES_STD_QUALIFIER endl; } } // // Delete the parser itself. Must be done prior to calling Terminate, below. // delete parser; XMLPlatformUtils::Terminate(); if (errorCount > 0) return false; else return true; } /************************************************************************/ /* class X3dViewCellsParseHandlers implementation */ /************************************************************************/ // --------------------------------------------------------------------------- // StdInParseHandlers: Constructors and Destructor // --------------------------------------------------------------------------- X3dViewCellsParseHandlers::X3dViewCellsParseHandlers(ViewCellsManager *viewCellsManager, const float viewCellHeight): mElementCount(0), mAttrCount(0), mCharacterCount(0), mSpaceCount(0), mViewCellsManager(viewCellsManager), mViewCellHeight(viewCellHeight) { } X3dViewCellsParseHandlers::~X3dViewCellsParseHandlers() { } // --------------------------------------------------------------------------- // StdInParseHandlers: Implementation of the SAX DocumentHandler interface // --------------------------------------------------------------------------- void X3dViewCellsParseHandlers::endElement(const XMLCh* const name) { StrX lname(name); string element(lname.LocalForm()); if (element == "Shape") EndShape(); } void X3dViewCellsParseHandlers::EndShape() { // currently processing no shape } void X3dViewCellsParseHandlers::StartIndexedFaceSet( AttributeList& attributes) { int len = attributes.getLength(); int i; for (i=0; i < len; i++) { string attrName(StrX(attributes.getName(i)).LocalForm()); if (attrName == "coordIndex") { StrX attrValue(attributes.getValue(i)); // handle coordIndex const char *ptr = attrValue.LocalForm(); char *endptr; while (1) { int index = strtol(ptr, &endptr, 10); if (ptr == endptr) break; if (index != -1) { mCurrentVertexIndices.push_back(index); } ptr = endptr; } } } } void X3dViewCellsParseHandlers::StartCoordinate(AttributeList& attributes) { int len = attributes.getLength(); VertexContainer vertices; int i; for (i=0; i < len; i++) { string attrName(StrX(attributes.getName(i)).LocalForm()); if (attrName == "point") { StrX attrValue(attributes.getValue(i)); const char *ptr = attrValue.LocalForm(); char *endptr; while (1) { float x = (float)strtod(ptr, &endptr); if (ptr == endptr) break; ptr = endptr; float y = (float)(float)strtod(ptr, &endptr); if (ptr == endptr) break; ptr = endptr; float z = (float)(float)strtod(ptr, &endptr); if (ptr == endptr) break; ptr = endptr; if (*ptr == ',') ptr++; Vector3 v(x, y, z); vertices.push_back(v); } } } for (i = 0; i < mCurrentVertexIndices.size(); i += 3) { Triangle3 baseTri(vertices[mCurrentVertexIndices[i + 0]], vertices[mCurrentVertexIndices[i + 1]], vertices[mCurrentVertexIndices[i + 2]]); // create view cell from base triangle mViewCellsManager->AddViewCell( mViewCellsManager->ExtrudeViewCell(baseTri, mViewCellHeight)); } } void X3dViewCellsParseHandlers::startElement(const XMLCh* const name, AttributeList& attributes) { StrX lname(name); string element(lname.LocalForm()); if (element == "IndexedFaceSet") { // create the viewcells from individual triangles StartIndexedFaceSet(attributes); } if (element == "Coordinate") { // add coordinates to the triangles StartCoordinate(attributes); } // do nothing //if (element == "Shape") {} // ignore material //if (element == "Material") {} ++ mElementCount; mAttrCount += attributes.getLength(); } void X3dViewCellsParseHandlers::characters(const XMLCh* const chars, const unsigned int length) { mCharacterCount += length; } void X3dViewCellsParseHandlers::ignorableWhitespace(const XMLCh* const chars, const unsigned int length) { mSpaceCount += length; } void X3dViewCellsParseHandlers::resetDocument() { mAttrCount = 0; mCharacterCount = 0; mElementCount = 0; mSpaceCount = 0; } // --------------------------------------------------------------------------- // StdInParseHandlers: Overrides of the SAX ErrorHandler interface // --------------------------------------------------------------------------- void X3dViewCellsParseHandlers::error(const SAXParseException& e) { XERCES_STD_QUALIFIER cerr << "\nError at (file " << StrX(e.getSystemId()) << ", line " << e.getLineNumber() << ", char " << e.getColumnNumber() << "): " << StrX(e.getMessage()) << XERCES_STD_QUALIFIER endl; } void X3dViewCellsParseHandlers::fatalError(const SAXParseException& e) { XERCES_STD_QUALIFIER cerr << "\nFatal Error at (file " << StrX(e.getSystemId()) << ", line " << e.getLineNumber() << ", char " << e.getColumnNumber() << "): " << StrX(e.getMessage()) << XERCES_STD_QUALIFIER endl; } void X3dViewCellsParseHandlers::warning(const SAXParseException& e) { XERCES_STD_QUALIFIER cerr << "\nWarning at (file " << StrX(e.getSystemId()) << ", line " << e.getLineNumber() << ", char " << e.getColumnNumber() << "): " << StrX(e.getMessage()) << XERCES_STD_QUALIFIER endl; } bool X3dParser::ParseFile(const string filename, ViewCellsManager &viewCells) { // Initialize the XML4C system try { XMLPlatformUtils::Initialize(); } catch (const XMLException& toCatch) { XERCES_STD_QUALIFIER cerr << "Error during initialization! Message:\n" << StrX(toCatch.getMessage()) << XERCES_STD_QUALIFIER endl; return false; } // // Create a SAX parser object. Then, according to what we were told on // the command line, set the options. // SAXParser* parser = new SAXParser; parser->setValidationScheme(valScheme); parser->setDoNamespaces(doNamespaces); parser->setDoSchema(doSchema); parser->setValidationSchemaFullChecking(schemaFullChecking); // // Create our SAX handler object and install it on the parser, as the // document and error handler. We are responsible for cleaning them // up, but since its just stack based here, there's nothing special // to do. // X3dViewCellsParseHandlers handler(&viewCells, mViewCellHeight); parser->setDocumentHandler(&handler); parser->setErrorHandler(&handler); unsigned long duration; int errorCount = 0; // create a faux scope so that 'src' destructor is called before // XMLPlatformUtils::Terminate { // // Kick off the parse and catch any exceptions. Create a standard // input input source and tell the parser to parse from that. // // StdInInputSource src; try { const unsigned long startMillis = XMLPlatformUtils::getCurrentMillis(); //GzBinFileInputStream str(filename.c_str()); parser->parse(filename.c_str()); const unsigned long endMillis = XMLPlatformUtils::getCurrentMillis(); duration = endMillis - startMillis; errorCount = parser->getErrorCount(); } catch (const OutOfMemoryException&) { XERCES_STD_QUALIFIER cerr << "OutOfMemoryException" << XERCES_STD_QUALIFIER endl; errorCount = 2; return false; } catch (const XMLException& e) { XERCES_STD_QUALIFIER cerr << "\nError during parsing: \n" << StrX(e.getMessage()) << "\n" << XERCES_STD_QUALIFIER endl; errorCount = 1; return false; } // Print out the stats that we collected and time taken if (!errorCount) { XERCES_STD_QUALIFIER cout << filename << ": " << duration << " ms (" << handler.GetElementCount() << " elems, " << handler.GetAttrCount() << " attrs, " << handler.GetSpaceCount() << " spaces, " << handler.GetCharacterCount() << " chars)" << XERCES_STD_QUALIFIER endl; } } // // Delete the parser itself. Must be done prior to calling Terminate, below. // delete parser; XMLPlatformUtils::Terminate(); if (errorCount > 0) return false; else return true; } }