Model::Entity* MapParser::parseEntity(const BBox& worldBounds, Utility::ProgressIndicator* indicator) { Token token = m_tokenizer.nextToken(); if (token.type() == TokenType::Eof) return NULL; expect(TokenType::OBrace | TokenType::CBrace, token); if (token.type() == TokenType::CBrace) return NULL; Model::Entity* entity = new Model::Entity(worldBounds); size_t firstLine = token.line(); while ((token = m_tokenizer.nextToken()).type() != TokenType::Eof) { switch (token.type()) { case TokenType::String: { String key = token.data(); expect(TokenType::String, token = m_tokenizer.nextToken()); String value = token.data(); entity->setProperty(key, value); break; } case TokenType::OBrace: { m_tokenizer.pushToken(token); bool moreBrushes = true; while (moreBrushes) { Model::Brush* brush = parseBrush(worldBounds, indicator); if (brush != NULL) entity->addBrush(*brush); expect(TokenType::OBrace | TokenType::CBrace, token = m_tokenizer.nextToken()); moreBrushes = (token.type() == TokenType::OBrace); m_tokenizer.pushToken(token); } break; } case TokenType::CBrace: { if (indicator != NULL) indicator->update(static_cast<int>(token.position())); entity->setFilePosition(firstLine, token.line() - firstLine); return entity; } default: delete entity; throw MapParserException(token, TokenType::String | TokenType::OBrace | TokenType::CBrace); } } return entity; }
Model::Brush* MapParser::parseBrush(const BBox& worldBounds, Utility::ProgressIndicator* indicator) { Token token = m_tokenizer.nextToken(); if (token.type() == TokenType::Eof) return NULL; expect(TokenType::OBrace | TokenType::CBrace, token); if (token.type() == TokenType::CBrace) return NULL; Model::Brush* brush = new Model::Brush(worldBounds); brush->setFilePosition(token.line()); while ((token = m_tokenizer.nextToken()).type() != TokenType::Eof) { switch (token.type()) { case TokenType::OParenthesis: { m_tokenizer.pushToken(token); Model::Face* face = parseFace(worldBounds); if (face != NULL && brush != NULL) { if (!brush->addFace(face)) { m_console.warn("Skipping malformed brush at line %i", brush->filePosition()); delete brush; brush = NULL; } } else { delete face; } break; } case TokenType::CBrace: if (indicator != NULL) indicator->update(static_cast<int>(token.position())); if (brush != NULL && !brush->closed()) { m_console.warn("Non-closed brush at line %i", brush->filePosition()); // delete brush; // brush = NULL; } return brush; default: delete brush; throw MapParserException(token, TokenType::OParenthesis | TokenType::CParenthesis); } } return NULL; }
Model::Brush* MapParser::parseBrush(const BBox& worldBounds, Utility::ProgressIndicator* indicator) { Token token = m_tokenizer.nextToken(); if (token.type() == TokenType::Eof) return NULL; expect(TokenType::OBrace | TokenType::CBrace, token); if (token.type() == TokenType::CBrace) return NULL; const size_t firstLine = token.line(); Model::FaceList faces; while ((token = m_tokenizer.nextToken()).type() != TokenType::Eof) { switch (token.type()) { case TokenType::OParenthesis: { m_tokenizer.pushToken(token); Model::Face* face = parseFace(worldBounds); if (face != NULL) faces.push_back(face); break; } case TokenType::CBrace: { if (indicator != NULL) indicator->update(static_cast<int>(token.position())); Model::Brush* brush = new Model::Brush(worldBounds); // sort the faces by the weight of their plane normals like QBSP does Model::FaceList sortedFaces = faces; std::sort(sortedFaces.begin(), sortedFaces.end(), Model::Face::WeightOrder(Plane::WeightOrder(true))); std::sort(sortedFaces.begin(), sortedFaces.end(), Model::Face::WeightOrder(Plane::WeightOrder(false))); Model::FaceList::iterator faceIt = sortedFaces.begin(); Model::FaceList::iterator faceEnd = sortedFaces.end(); while (faceIt != faceEnd) { Model::Face* face = *faceIt++; if (!brush->addFace(face)) { m_console.warn("Skipping malformed brush at line %i", firstLine); delete brush; brush = NULL; break; } } // if something went wrong, we must delete all faces that have not been added to the brush yet if (faceIt != faceEnd) Utility::deleteAll(sortedFaces, faceIt); if (brush != NULL) { brush->setFilePosition(firstLine, token.line() - firstLine); if (!brush->closed()) m_console.warn("Non-closed brush at line %i", firstLine); } return brush; } default: { Utility::deleteAll(faces); throw MapParserException(token, TokenType::OParenthesis | TokenType::CParenthesis); } } } return NULL; }