From 6c297d44abbef4a4be05061152bc7169e8db244b Mon Sep 17 00:00:00 2001 From: suhrke Date: Sat, 8 Jul 2017 07:20:55 -0700 Subject: [PATCH] bug fixes to get pocket-infinity converted --- ReflexToQ3/Makefile | 4 +- ReflexToQ3/includes/EntityConverter.cpp | 165 ++++++++++++++++-------- ReflexToQ3/includes/EntityConverter.hpp | 9 ++ ReflexToQ3/test/catch.cpp | 56 +++++++- 4 files changed, 171 insertions(+), 63 deletions(-) diff --git a/ReflexToQ3/Makefile b/ReflexToQ3/Makefile index 701b505..c47ea46 100644 --- a/ReflexToQ3/Makefile +++ b/ReflexToQ3/Makefile @@ -1,6 +1,6 @@ EX=reflex2q3 CC=g++ -CFLAGS=-std=c++11 -I"./includes" -I"./includes/Catch/single_include" -I"./includes/cxxopts/include" -I"/usr/include/eigen3" +CFLAGS=-std=c++11 -I"./includes" -I"./includes/Catch/single_include" -I"./includes/cxxopts/include" -I"/usr/include/eigen3" -g TESTEX=test/test-parser UNITEX=test/catch @@ -35,5 +35,5 @@ EntityConverter.o: includes/EntityConverter.cpp $(CC) -c $^ $(CFLAGS) clean: - rm *.o *.log $(EX) $(TESTEX) $(UNITEX) + rm *.o *.log $(EX) $(TESTEX) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index d54bc48..42507ee 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -25,10 +25,14 @@ #include + + /*----------------------------------------------------------------------------- * PUBLIC *-----------------------------------------------------------------------------*/ + + EntityConverter::EntityConverter(const std::string &entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50.0), OUTPUT_PRECISION(10) @@ -73,7 +77,7 @@ EntityConverter::EntityConverter(const std::string &entityMapFile, } } else { - throw std::ios::failure( "Error: EntityConverter failed to open .map file" ); + throw std::ios::failure( "Error: EntityConverter failed to open .map file " + reflexMapFile ); } fin.close(); haveMapInfo_ = true; @@ -149,12 +153,14 @@ EntityConverter::convert(const std::vector &lines) std::string type; if ( lines.size() < 1 ) { - throw std::runtime_error("error: empty entity cannot be converted"); + throw std::runtime_error( + makeErrorMessage( "error: empty entity cannot be converted", lines )); } // second token is the type std::istringstream iss(lines[0]); if ( ! (iss >> trash >> type)) { - throw std::runtime_error("error: type is required"); + throw std::runtime_error( + makeErrorMessage( "error: type is required", lines )); } // If worldspawn, first reenable all gamemodes @@ -213,7 +219,8 @@ EntityConverter::convert(const std::vector &lines) } } else { - throw std::runtime_error( "error: Map info must be extracted prior to conversion" ); + throw std::runtime_error( + makeErrorMessage( "error: Map info must be extracted prior to conversion", lines )); } // If unsupported entity, return empty vector @@ -223,9 +230,13 @@ EntityConverter::convert(const std::vector &lines) + /*----------------------------------------------------------------------------- * PROTECTED *-----------------------------------------------------------------------------*/ + + + std::vector EntityConverter::convertPickup(const std::vector &lines) const { @@ -238,7 +249,8 @@ EntityConverter::convertPickup(const std::vector &lines) const bool havePickupID = false; if ( lines.size() < 3 ) { - throw std::runtime_error("error: Pickup entity requires at least 3 lines"); + throw std::runtime_error( + makeErrorMessage( "error: Pickup entity requires at least 3 lines", lines )); } for (int i = 1; i < lines.size(); i++) { @@ -248,7 +260,8 @@ EntityConverter::convertPickup(const std::vector &lines) const // Vector3 position coord0 coord1 coord2 if ( ! (iss >> trash >> trash >> coords[0] >> coords[1] >> coords[2])) { - throw std::runtime_error("error: Pickup entity requires coordinates"); + throw std::runtime_error( + makeErrorMessage( "error: Pickup entity requires coordinates", lines )); } havePosition = true; } @@ -256,7 +269,8 @@ EntityConverter::convertPickup(const std::vector &lines) const std::istringstream iss(lines[i]); // UInt8 pickupType ID if ( ! (iss >> trash >> trash >> pickupID) ) { - throw std::runtime_error("error: Pickup entity requires Pickup ID"); + throw std::runtime_error( + makeErrorMessage( "error: Pickup entity requires Pickup ID", lines )); } havePickupID = true; } @@ -265,7 +279,8 @@ EntityConverter::convertPickup(const std::vector &lines) const if ( havePosition && havePickupID ) { auto pickupIter = pickupMap_.find(pickupID); if ( pickupIter == pickupMap_.end() ) { - throw std::runtime_error("error: Pickup ID must be valid"); + throw std::runtime_error( + makeErrorMessage( "error: Pickup ID must be valid", lines )); } std::stringstream pickupStream; pickupStream << "\"classname\" \"" << pickupIter->second << "\"" << std::endl; @@ -278,7 +293,8 @@ EntityConverter::convertPickup(const std::vector &lines) const return convertedLines; } else { - throw std::runtime_error("error: Pickup requires position and pickup ID, missing 1 or both"); + throw std::runtime_error( + makeErrorMessage( "error: Pickup requires position and pickup ID, missing 1 or both", lines )); } } @@ -314,7 +330,8 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) const if ( lines.size() < 2 ) { - throw std::runtime_error("error: PlayerSpawn entity requires at least 2 lines"); + throw std::runtime_error( + makeErrorMessage( "error: PlayerSpawn entity requires at least 2 lines", lines )); } for (int i = 1; i < lines.size(); i++) { @@ -324,7 +341,8 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) const // Vector3 position coord0 coord1 coord2 if ( ! (iss >> trash >> trash >> coords[0] >> coords[1] >> coords[2])) { - throw std::runtime_error("error: PlayerSpawn entity requires position coordinates"); + throw std::runtime_error( + makeErrorMessage( "error: PlayerSpawn entity requires position coordinates", lines )); } havePosition = true; } @@ -332,7 +350,8 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) const std::istringstream iss(lines[i]); // UInt8 pickupType ID if ( ! (iss >> trash >> trash >> angle )) { - throw std::runtime_error("error: Pickup entity requires Pickup ID"); + throw std::runtime_error( + makeErrorMessage( "error: Pickup entity requires Pickup ID", lines )); } } // Bool8 modeX 0 indicates this spawn is not for game mode X @@ -392,7 +411,8 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) const return convertedLines; } else { - throw std::runtime_error("error: PlayerSpawn entity requires position coordinates"); + throw std::runtime_error( + makeErrorMessage( "error: PlayerSpawn entity requires position coordinates", lines )); } } @@ -406,12 +426,14 @@ EntityConverter::convertJumpPad(const std::vector &lines) const std::string trash; if ( lines.size() < 2 ) { - throw std::runtime_error("error: JumpPad entity requires at least 2 lines"); + throw std::runtime_error( + makeErrorMessage( "error: JumpPad entity requires at least 2 lines", lines )); } std::istringstream iss(lines[1]); // String32 target targetName if ( ! (iss >> trash >> trash >> targetName) ) { - throw std::runtime_error("error: JumpPad entity requires target name"); + throw std::runtime_error( + makeErrorMessage( "error: JumpPad entity requires target name", lines )); } convertedLines.push_back ( "\"classname\" \"trigger_push\"\n" ); @@ -431,12 +453,14 @@ EntityConverter::convertTeleporter(const std::vector &lines) const std::string trash; if ( lines.size() < 2 ) { - throw std::runtime_error("error: Teleport entity requires at least 2 lines"); + throw std::runtime_error( + makeErrorMessage( "error: Teleport entity requires at least 2 lines", lines )); } std::istringstream iss(lines[1]); // String32 target targetName if ( ! (iss >> trash >> trash >> targetName) ) { - throw std::runtime_error( "error: Teleport entity requires target name" ); + throw std::runtime_error( + makeErrorMessage( "error: Teleport entity requires target name", lines )); } convertedLines.push_back ( "\"classname\" \"trigger_teleport\"\n" ); @@ -463,7 +487,8 @@ EntityConverter::convertTarget(const std::vector &lines) const if ( lines.size() < 3 ) { - throw std::runtime_error("error: Target entity requires at least 3 lines"); + throw std::runtime_error( + makeErrorMessage( "error: Target entity requires at least 3 lines", lines )); } for (int i = 1; i < lines.size(); i++) { @@ -473,7 +498,8 @@ EntityConverter::convertTarget(const std::vector &lines) const // Vector3 position coord0 coord1 coord2 if ( ! (iss >> trash >> trash >> coords[0] >> coords[1] >> coords[2])) { - throw std::runtime_error( "error: Target entity requires coordinates" ); + throw std::runtime_error( + makeErrorMessage( "error: Target entity requires coordinates", lines )); } havePosition = true; } @@ -481,7 +507,8 @@ EntityConverter::convertTarget(const std::vector &lines) const std::istringstream iss(lines[i]); // UInt8 name uniqueName if ( ! (iss >> trash >> trash >> targetName) ) { - throw std::runtime_error( "error: Target entity requires target name" ); + throw std::runtime_error( + makeErrorMessage( "error: Target entity requires target name", lines )); } haveName = true; } @@ -489,7 +516,8 @@ EntityConverter::convertTarget(const std::vector &lines) const std::istringstream iss(lines[i]); // Vector3 angles angle notapplicable notapplicable if ( ! (iss >> trash >> trash >> angle) ) { - throw std::runtime_error( "error: Target entity requires target angle if specified" ); + throw std::runtime_error( + makeErrorMessage( "error: Target entity requires target angle if specified", lines )); } haveAngle = true; } @@ -499,14 +527,8 @@ EntityConverter::convertTarget(const std::vector &lines) const if ( havePosition && haveName) { auto targetIter = targetMap_.find(targetName); if ( targetIter == targetMap_.end() ) { - std::cerr << "EntityConverter doesn't know what the source of a Target entity with the " - << "following attributes. It is probably an unsupported entity type or feature. " - << "(game over camera, etc). Returning an empty vector." << std::endl; - std::vector::const_iterator it; - for ( it=lines.begin(); it!=lines.end(); ++it ) { - std::cerr << *it << std::endl; - } - std::cerr << std::endl; + std::cerr << makeErrorMessage("EntityConverter doesn't know what the source of a Target entity with the following attributes. This entity will not be converted. It is probably an unsupported entity type or feature. (e.g. game over camera)", lines); + std::vector empty; return empty; } @@ -540,7 +562,8 @@ EntityConverter::convertTarget(const std::vector &lines) const return convertedLines; } else { - throw std::runtime_error("error: Target entity requires position coordinates and targetname"); + throw std::runtime_error( + makeErrorMessage( "error: Target entity requires position coordinates and targetname", lines )); } } @@ -577,17 +600,19 @@ EntityConverter::convertPointLight(const std::vector &lines) const std::vector convertedLines; //position and intensity required, color optional std::string coords[3]; - std::string intensity; + //default to a typical value + std::string intensity = "1.0"; //color is hex 8 digits - std::string color; + //default to white if no color specified + std::string color = "ff000000"; std::string trash; bool havePosition = false; - bool haveIntensity = false; - bool haveColor = false; + bool haveColor = false; - if ( lines.size() < 3 ) { - throw std::runtime_error("error: PointLight entity requires at least 3 lines"); + if ( lines.size() < 2 ) { + throw std::runtime_error( + makeErrorMessage( "error: PointLight entity requires at least 2 lines", lines )); } for (int i = 1; i < lines.size(); i++) { @@ -597,7 +622,8 @@ EntityConverter::convertPointLight(const std::vector &lines) const // Vector3 position coord0 coord1 coord2 if ( ! (iss >> trash >> trash >> coords[0] >> coords[1] >> coords[2])) { - throw std::runtime_error( "error: PointLight entity requires position coordinates" ); + throw std::runtime_error( + makeErrorMessage( "error: PointLight entity requires valid position coordinates", lines )); } havePosition = true; } @@ -605,15 +631,16 @@ EntityConverter::convertPointLight(const std::vector &lines) const std::istringstream iss(lines[i]); // Float intensity validFloat if ( ! (iss >> trash >> trash >> intensity) ) { - throw std::runtime_error( "error: PointLight entity requires intensity value" ); + throw std::runtime_error( + makeErrorMessage( "error: PointLight intensity keyword must be followed by a value", lines )); } - haveIntensity = true; } else if ( type == "color" ) { std::istringstream iss(lines[i]); // ColourXRGB32 color eightDigitHexValue if ( ! (iss >> trash >> trash >> color) ) { - throw std::runtime_error( "error: PointLight entity requires valid float value if specified" ); + throw std::runtime_error( + makeErrorMessage( "error: PointLight color keyword must be followed by a value", lines )); } haveColor = true; } @@ -621,7 +648,7 @@ EntityConverter::convertPointLight(const std::vector &lines) const } - if ( havePosition && haveIntensity) { + if ( havePosition ) { convertedLines.push_back ( "\"classname\" \"light\"\n" ); // coordinates reordered to x, z, y std::stringstream positionStream; @@ -633,27 +660,30 @@ EntityConverter::convertPointLight(const std::vector &lines) const intensityStream << "\"light\" \"" << adjustBrightness(intensity) << "\"\n"; convertedLines.push_back ( intensityStream.str() ); + float red; + float green; + float blue; if ( haveColor ) { - std::stringstream colorStream; - float red; - float green; - float blue; // Convert 32bit hex RGBA value (ALPHA ALWAYS FULL) into RGB values hexToRGB(color, red, green, blue); - colorStream << "\"_color\" \"" << red << " " << green << " " << blue << "\"" << std::endl; - convertedLines.push_back (colorStream.str() ); } + + std::stringstream colorStream; + colorStream << "\"_color\" \"" << red << " " << green << " " << blue << "\"" << std::endl; + convertedLines.push_back (colorStream.str() ); + return convertedLines; } else { - throw std::runtime_error("error: Target entity requires position coordinates and targetname"); + throw std::runtime_error( + makeErrorMessage( "error: PointLight entity requires position coordinates", lines )); } } -/*----------------------------------------------------------------------------- - * PRIVATE - *-----------------------------------------------------------------------------*/ + + + std::string EntityConverter::getAttributeType(const std::string &line) const { @@ -684,7 +714,7 @@ EntityConverter::mapEntities(const std::string &mapFile) int id; std::string pickup; if ( ! (iss >> id >> pickup)) { - throw std::runtime_error( "format error in .ent file" ); + throw std::runtime_error( "format error in Pickup .pck file " + mapFile ); } pickupMap_.insert ( std::pair(id, pickup) ); } @@ -708,7 +738,7 @@ EntityConverter::extractFromEntity(const std::string &line, std::istream &is) std::getline(is, nextLine); std::istringstream iss(nextLine); if ( ! (iss >> trash >> trash >> targetName)) { - throw std::runtime_error( "format error in .map file"); + throw std::runtime_error( "Format error in .map file" ); } targetMap_.insert ( std::pair(targetName, "Teleporter") ); } @@ -716,7 +746,7 @@ EntityConverter::extractFromEntity(const std::string &line, std::istream &is) std::getline(is, nextLine); std::istringstream iss(nextLine); if ( ! (iss >> trash >> trash >> targetName)) { - throw std::runtime_error( "format error in .map file"); + throw std::runtime_error( "Format error in .map file" ); } targetMap_.insert ( std::pair(targetName, "JumpPad") ); } @@ -784,6 +814,33 @@ EntityConverter::adjustBrightness(const std::string &value) const + +std::string +EntityConverter::makeErrorMessage(const std::string message, + const std::vector entity) const +{ + std::stringstream ss; + ss << std::endl << message << std::endl; + + std::vector::const_iterator it; + for ( it=entity.begin(); it!=entity.end(); ++it ) { + ss << *it << std::endl; + } + + ss << std::endl; + + return ss.str(); +} + + + + +/*----------------------------------------------------------------------------- + * PRIVATE + *-----------------------------------------------------------------------------*/ + + + // DEBUG void EntityConverter::printMapping() const diff --git a/ReflexToQ3/includes/EntityConverter.hpp b/ReflexToQ3/includes/EntityConverter.hpp index 43da58b..0b7bba0 100644 --- a/ReflexToQ3/includes/EntityConverter.hpp +++ b/ReflexToQ3/includes/EntityConverter.hpp @@ -206,6 +206,15 @@ class EntityConverter *-------------------------------------------------------------------------------------- */ int adjustBrightness(const std::string &value) const; + /* + *-------------------------------------------------------------------------------------- + * Class: EntityConverter + * Method: EntityConverter :: makeErrorMessage + * Description: Combine a message and the entity responsible into an error message + *-------------------------------------------------------------------------------------- + */ + std::string makeErrorMessage(const std::string message, + const std::vector entity) const; diff --git a/ReflexToQ3/test/catch.cpp b/ReflexToQ3/test/catch.cpp index 00199d5..931d415 100644 --- a/ReflexToQ3/test/catch.cpp +++ b/ReflexToQ3/test/catch.cpp @@ -431,6 +431,50 @@ TEST_CASE( "r2x: a single PointLight entity can be converted", "[EntityConverter REQUIRE( converted[1] == "\"origin\" \"-216.00000 -1488.000488 -132.00000\"\n" ); REQUIRE( converted[2] == "\"light\" \"75\"\n" ); + INFO( converted[3] ); + std::istringstream iss(converted[3]); + std::string attribute; + std::string r; + float red; + float green; + float blue; + iss >> attribute >> r >> green >> blue; + r.erase(r.begin()); //removing preceding quote is necessary + std::stringstream redStream(r); + redStream >> red; + + REQUIRE( attribute == "\"_color\"" ); + + REQUIRE( fabs( 1.0 - red) <= DELTA ); + REQUIRE( fabs( 0.768627 - green) <= DELTA ); + REQUIRE( fabs( 0.0 - blue) <= DELTA ); +} + + +TEST_CASE( "r2x: PointLight defaults to white light of typical intensity", "[EntityConverter]" ) { + + // Instantiate object + EntityConverter ec (PICKUP_FILENAME); + + // Mock up entity + std::vector entity; + entity.push_back(" type PointLight"); + entity.push_back(" Vector3 position -216.00000 -132.00000 -1488.000488"); + + // Mock up entity queue + std::queue> q; + q.push( entity ); + + // Match related entities (none) + ec.extractMapInfo( q ); + + // Convert a single entity + std::vector converted = ec.convert(entity); + + REQUIRE( converted[0] == "\"classname\" \"light\"\n" ); + REQUIRE( converted[1] == "\"origin\" \"-216.00000 -1488.000488 -132.00000\"\n" ); + REQUIRE( converted[2] == "\"light\" \"50\"\n" ); + INFO( converted[3] ); std::istringstream iss(converted[3]); std::string attribute; @@ -444,10 +488,11 @@ TEST_CASE( "r2x: a single PointLight entity can be converted", "[EntityConverter redStream >> red; REQUIRE( attribute == "\"_color\"" ); - - REQUIRE( fabs(1.0 - red) <= DELTA ); - REQUIRE( fabs(0.768627 - green) <= DELTA ); - REQUIRE( fabs(0.0 - blue) <= DELTA ); + + REQUIRE( fabs( 0.0 - red) <= DELTA ); + REQUIRE( fabs( 0.0 - green) <= DELTA ); + REQUIRE( fabs( 0.0 - blue) <= DELTA ); + } @@ -457,8 +502,5 @@ TEST_CASE( "r2x: a single PointLight entity can be converted", "[EntityConverter - - - #endif //CATCH_CONFIG_MAIN