mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	- Fixed a few bugs related to the terrain view generation - Updated CMakeLists in /test(copy resources if no source files has been changed too)
This commit is contained in:
		| @@ -48,30 +48,33 @@ | ||||
| 			"id" : "s1", | ||||
| 			"data" : | ||||
| 			[ | ||||
| 				"?", "?", "T", | ||||
| 				"?", "N", "N", | ||||
| 				"T,N-1", "T,N-2", "T,N-3", | ||||
| 				"T,N-2", "N", "N", | ||||
| 				"T", "N", "N" | ||||
| 			], | ||||
| 			"maxPoints" : 3, | ||||
| 			"mapping" : "0-3, 20-23" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"id" : "s2", | ||||
| 			"data" : | ||||
| 			[ | ||||
| 				"?", "N", "N", | ||||
| 				"T", "N", "N", | ||||
| 				"?", "N", "N" | ||||
| 				"D-1,S-2,N", "N", "N", | ||||
| 				"T,D-2", "N", "N", | ||||
| 				"D-1,S-2,N", "N", "N" | ||||
| 			], | ||||
| 			"maxPoints" : 4, | ||||
| 			"mapping" : "4-7, 24-27" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"id" : "s3", | ||||
| 			"data" : | ||||
| 			[ | ||||
| 				"?", "T", "?", | ||||
| 				"D-1,S-2,N", "T,D-2", "D-1,S-2,N", | ||||
| 				"N", "N", "N", | ||||
| 				"N", "N", "N" | ||||
| 			], | ||||
| 			"maxPoints" : 4, | ||||
| 			"mapping" : "8-11, 28-31" | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -79,18 +82,19 @@ | ||||
| 			"data" : | ||||
| 			[ | ||||
| 				"N", "N", "N", | ||||
| 				"N", "N", "N", | ||||
| 				"N", "N", "T" | ||||
| 				"N", "N", "s3-1", | ||||
| 				"N", "s2-1", "T" | ||||
| 			], | ||||
| 			"mapping" : "12-15, 32-35" | ||||
| 			"mapping" : "12-15, 32-35", | ||||
| 			"minPoints" : 2 | ||||
| 		}, | ||||
| 		{ | ||||
| 			"id" : "s5", | ||||
| 			"data" : | ||||
| 			[ | ||||
| 				"T", "T", "s5-1,?", | ||||
| 				"T", "N", "N", | ||||
| 				"s5-1,?", "N", "N" | ||||
| 				"T", "T", "?", | ||||
| 				"T", "N", "s6-1,m1-1,m2-1,N", | ||||
| 				"?", "s6-1,m1-1,m2-1,N", "N" | ||||
| 			], | ||||
| 			"mapping" : "16-17, 36-37", | ||||
| 			"minPoints" : 1 | ||||
| @@ -133,7 +137,7 @@ | ||||
| 			[ | ||||
| 				"N", "N", "D,N", | ||||
| 				"N", "N", "D", | ||||
| 				"S", "D", "D,N" | ||||
| 				"S", "D,N", "D,N" | ||||
| 			], | ||||
| 			"mapping" : "43" | ||||
| 		}, | ||||
| @@ -151,7 +155,7 @@ | ||||
| 			"id" : "m5", | ||||
| 			"data" : | ||||
| 			[ | ||||
| 				"N", "N", "D,N", | ||||
| 				"N", "N", "D", | ||||
| 				"N", "N", "D", | ||||
| 				"N", "N", "S" | ||||
| 			], | ||||
| @@ -171,10 +175,11 @@ | ||||
| 			"id" : "m7", | ||||
| 			"data" : | ||||
| 			[ | ||||
| 				"N", "N", "D,S,N", | ||||
| 				"N", "N", "?", | ||||
| 				"N", "N", "S", | ||||
| 				"D", "D", "D,S,N" | ||||
| 				"D-1,N", "D-1,N", "?" | ||||
| 			], | ||||
| 			"minPoints" : 1, | ||||
| 			"mapping" : "47" | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -183,7 +188,7 @@ | ||||
| 			[ | ||||
| 				"N", "N", "D", | ||||
| 				"N", "N", "D", | ||||
| 				"D,S,N", "S", "D,S,N" | ||||
| 				"?", "S", "?" | ||||
| 			], | ||||
| 			"mapping" : "48" | ||||
| 		}, | ||||
| @@ -237,8 +242,8 @@ | ||||
| 			"data" : | ||||
| 			[ | ||||
| 				"D", "D", "D", | ||||
| 				"D", "N", "N", | ||||
| 				"D", "N", "S" | ||||
| 				"D", "N", "?", | ||||
| 				"D", "?", "S" | ||||
| 			], | ||||
| 			"mapping" : "12-15" | ||||
| 		}, | ||||
| @@ -347,11 +352,12 @@ | ||||
| 			"id" : "s5", | ||||
| 			"data" : | ||||
| 			[ | ||||
| 				"S", "S", "N", | ||||
| 				"S", "S", "N,S-1", | ||||
| 				"S", "N", "N", | ||||
| 				"N", "N", "N" | ||||
| 				"N,S-1", "N", "N" | ||||
| 			], | ||||
| 			"mapping" : "16-17", | ||||
| 			"maxPoints" : 1 | ||||
| 		}, | ||||
| 		{ | ||||
| 			"id" : "s6", | ||||
|   | ||||
| @@ -301,7 +301,10 @@ CTerrainViewPatternConfig::CTerrainViewPatternConfig() | ||||
|  | ||||
| 			// Read optional attributes | ||||
| 			pattern.id = ptrnNode["id"].String(); | ||||
| 			assert(!pattern.id.empty()); | ||||
| 			pattern.minPoints = static_cast<int>(ptrnNode["minPoints"].Float()); | ||||
| 			pattern.maxPoints = static_cast<int>(ptrnNode["maxPoints"].Float()); | ||||
| 			if(pattern.maxPoints == 0) pattern.maxPoints = std::numeric_limits<int>::max(); | ||||
| 			pattern.flipMode = ptrnNode["flipMode"].String(); | ||||
| 			if(pattern.flipMode.empty()) | ||||
| 			{ | ||||
| @@ -388,65 +391,59 @@ std::string CDrawTerrainOperation::getLabel() const | ||||
|  | ||||
| void CDrawTerrainOperation::updateTerrainViews(const MapRect & rect) | ||||
| { | ||||
| 	for(int i = rect.x; i < rect.x + rect.width; ++i) | ||||
| 	for(int x = rect.x; x < rect.x + rect.width; ++x) | ||||
| 	{ | ||||
| 		for(int j = rect.y; j < rect.y + rect.height; ++j) | ||||
| 		for(int y = rect.y; y < rect.y + rect.height; ++y) | ||||
| 		{ | ||||
| 			const auto & patterns = | ||||
| 					CTerrainViewPatternConfig::get().getPatternsForGroup(getTerrainGroup(map->getTile(int3(i, j, rect.z)).terType)); | ||||
| 					CTerrainViewPatternConfig::get().getPatternsForGroup(getTerrainGroup(map->getTile(int3(x, y, rect.z)).terType)); | ||||
|  | ||||
| 			// Detect a pattern which fits best | ||||
| 			int bestPattern = -1, bestFlip = -1; | ||||
| 			std::string transitionReplacement; | ||||
| 			int bestPattern = -1; | ||||
| 			ValidationResult valRslt(false); | ||||
| 			for(int k = 0; k < patterns.size(); ++k) | ||||
| 			{ | ||||
| 				const auto & pattern = patterns[k]; | ||||
|  | ||||
| 				for(int flip = 0; flip < 4; ++flip) | ||||
| 				valRslt = validateTerrainView(int3(x, y, rect.z), pattern); | ||||
| 				if(valRslt.result) | ||||
| 				{ | ||||
| 					auto valRslt = validateTerrainView(int3(i, j, rect.z), flip > 0 ? getFlippedPattern(pattern, flip) : pattern); | ||||
| 					if(valRslt.result) | ||||
| 					{ | ||||
| 						logGlobal->debugStream() << "Pattern detected at pos " << i << "x" << j << "x" << rect.z << ": P-Nr. " << k | ||||
| 							  << ", Flip " << flip << ", Repl. " << valRslt.transitionReplacement; | ||||
|  | ||||
| 						bestPattern = k; | ||||
| 						bestFlip = flip; | ||||
| 						transitionReplacement = valRslt.transitionReplacement; | ||||
| 						break; | ||||
| 					} | ||||
| 					logGlobal->debugStream() << "Pattern detected at pos " << x << "x" << y << "x" << rect.z << ": P-Nr. " << pattern.id | ||||
| 						  << ", Flip " << valRslt.flip << ", Repl. " << valRslt.transitionReplacement; | ||||
| 					bestPattern = k; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			//assert(bestPattern != -1); | ||||
| 			if(bestPattern == -1) | ||||
| 			{ | ||||
| 				// This shouldn't be the case | ||||
| 				logGlobal->warnStream() << "No pattern detected at pos " << i << "x" << j << "x" << rect.z; | ||||
| 				logGlobal->warnStream() << "No pattern detected at pos " << x << "x" << y << "x" << rect.z; | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 			// Get mapping | ||||
| 			const TerrainViewPattern & pattern = patterns[bestPattern]; | ||||
| 			std::pair<int, int> mapping; | ||||
| 			if(transitionReplacement.empty()) | ||||
| 			if(valRslt.transitionReplacement.empty()) | ||||
| 			{ | ||||
| 				mapping = pattern.mapping[0]; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				mapping = transitionReplacement == TerrainViewPattern::RULE_DIRT ? pattern.mapping[0] : pattern.mapping[1]; | ||||
| 				mapping = valRslt.transitionReplacement == TerrainViewPattern::RULE_DIRT ? pattern.mapping[0] : pattern.mapping[1]; | ||||
| 			} | ||||
|  | ||||
| 			// Set terrain view | ||||
| 			auto & tile = map->getTile(int3(i, j, rect.z)); | ||||
| 			auto & tile = map->getTile(int3(x, y, rect.z)); | ||||
| 			if(pattern.flipMode == TerrainViewPattern::FLIP_MODE_SAME_IMAGE) | ||||
| 			{ | ||||
| 				tile.terView = gen->getInteger(mapping.first, mapping.second); | ||||
| 				tile.extTileFlags = bestFlip; | ||||
| 				tile.extTileFlags = valRslt.flip; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				const int framesPerRot = 2; | ||||
| 				int firstFrame = mapping.first + bestFlip * framesPerRot; | ||||
| 				int firstFrame = mapping.first + valRslt.flip * framesPerRot; | ||||
| 				tile.terView = gen->getInteger(firstFrame, firstFrame + framesPerRot - 1); | ||||
| 				tile.extTileFlags =	0; | ||||
| 			} | ||||
| @@ -472,6 +469,20 @@ ETerrainGroup::ETerrainGroup CDrawTerrainOperation::getTerrainGroup(ETerrainType | ||||
| } | ||||
|  | ||||
| CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainView(const int3 & pos, const TerrainViewPattern & pattern, int recDepth /*= 0*/) const | ||||
| { | ||||
| 	for(int flip = 0; flip < 4; ++flip) | ||||
| 	{ | ||||
| 		auto valRslt = validateTerrainViewInner(pos, flip > 0 ? getFlippedPattern(pattern, flip) : pattern, recDepth); | ||||
| 		if(valRslt.result) | ||||
| 		{ | ||||
| 			valRslt.flip = flip; | ||||
| 			return valRslt; | ||||
| 		} | ||||
| 	} | ||||
| 	return ValidationResult(false); | ||||
| } | ||||
|  | ||||
| CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainViewInner(const int3 & pos, const TerrainViewPattern & pattern, int recDepth /*= 0*/) const | ||||
| { | ||||
| 	ETerrainType centerTerType = map->getTile(pos).terType; | ||||
| 	int totalPoints = 0; | ||||
| @@ -488,15 +499,16 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi | ||||
| 		// Get terrain group of the current cell | ||||
| 		int cx = pos.x + (i % 3) - 1; | ||||
| 		int cy = pos.y + (i / 3) - 1; | ||||
| 		int3 currentPos(cx, cy, pos.z); | ||||
| 		bool isAlien = false; | ||||
| 		ETerrainType terType; | ||||
| 		if(cx < 0 || cx >= map->width || cy < 0 || cy >= map->height) | ||||
| 		if(!map->isInTheMap(currentPos)) | ||||
| 		{ | ||||
| 			terType = centerTerType; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			terType = map->getTile(int3(cx, cy, pos.z)).terType; | ||||
| 			terType = map->getTile(currentPos).terType; | ||||
| 			if(terType != centerTerType) | ||||
| 			{ | ||||
| 				isAlien = true; | ||||
| @@ -512,17 +524,13 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi | ||||
| 			{ | ||||
| 				if(recDepth == 0) | ||||
| 				{ | ||||
| 					const auto & patternForRule = CTerrainViewPatternConfig::get().getPatternById(pattern.terGroup, rule.name); | ||||
| 					auto rslt = validateTerrainView(int3(cx, cy, pos.z), patternForRule, 1); | ||||
| 					if(!rslt.result) | ||||
| 					if(map->isInTheMap(currentPos) && terType == centerTerType) | ||||
| 					{ | ||||
| 						return ValidationResult(false); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						topPoints = std::max(topPoints, rule.points); | ||||
| 						continue; | ||||
| 						const auto & patternForRule = CTerrainViewPatternConfig::get().getPatternById(pattern.terGroup, rule.name); | ||||
| 						auto rslt = validateTerrainView(currentPos, patternForRule, 1); | ||||
| 						if(rslt.result) topPoints = std::max(topPoints, rule.points); | ||||
| 					} | ||||
| 					continue; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| @@ -549,14 +557,20 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi | ||||
| 						|| rule.name == TerrainViewPattern::RULE_ANY) | ||||
| 						&& isSandType(terType); | ||||
|  | ||||
| 				if(transitionReplacement.empty() && (rule.name == TerrainViewPattern::RULE_TRANSITION | ||||
| 					|| rule.name == TerrainViewPattern::RULE_ANY) && (dirtTestOk || sandTestOk)) | ||||
| 				if(transitionReplacement.empty() && rule.name == TerrainViewPattern::RULE_TRANSITION | ||||
| 						&& (dirtTestOk || sandTestOk)) | ||||
| 				{ | ||||
| 					transitionReplacement = dirtTestOk ? TerrainViewPattern::RULE_DIRT : TerrainViewPattern::RULE_SAND; | ||||
| 				} | ||||
| 				applyValidationRslt((dirtTestOk && transitionReplacement != TerrainViewPattern::RULE_SAND) | ||||
| 						|| (sandTestOk && transitionReplacement != TerrainViewPattern::RULE_DIRT) | ||||
| 						|| nativeTestOk); | ||||
| 				if(rule.name == TerrainViewPattern::RULE_TRANSITION) | ||||
| 				{ | ||||
| 					applyValidationRslt((dirtTestOk && transitionReplacement != TerrainViewPattern::RULE_SAND) || | ||||
| 							(sandTestOk && transitionReplacement != TerrainViewPattern::RULE_DIRT)); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					applyValidationRslt(dirtTestOk || sandTestOk || nativeTestOk); | ||||
| 				} | ||||
| 			} | ||||
| 			else if(pattern.terGroup == ETerrainGroup::DIRT) | ||||
| 			{ | ||||
| @@ -593,12 +607,14 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(pattern.minPoints > totalPoints) | ||||
| 	if(totalPoints >= pattern.minPoints && totalPoints <= pattern.maxPoints) | ||||
| 	{ | ||||
| 		return ValidationResult(true, transitionReplacement); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return ValidationResult(false); | ||||
| 	} | ||||
|  | ||||
| 	return ValidationResult(true, transitionReplacement); | ||||
| } | ||||
|  | ||||
| bool CDrawTerrainOperation::isSandType(ETerrainType terType) const | ||||
|   | ||||
| @@ -190,8 +190,8 @@ struct DLL_LINKAGE TerrainViewPattern | ||||
| 	/// std::pair   -> 1st value: lower range, 2nd value: upper range | ||||
| 	std::vector<std::pair<int, int> > mapping; | ||||
|  | ||||
| 	/// The minimum points to reach to to validate the pattern successfully. | ||||
| 	int minPoints; | ||||
| 	/// The minimum and maximum points to reach to validate the pattern successfully. | ||||
| 	int minPoints, maxPoints; | ||||
|  | ||||
| 	/// Describes if flipping is required and which mapping should be used. | ||||
| 	std::string flipMode; | ||||
| @@ -236,12 +236,15 @@ private: | ||||
| 		bool result; | ||||
| 		/// The replacement of a T rule, either D or S. | ||||
| 		std::string transitionReplacement; | ||||
| 		int flip; | ||||
| 	}; | ||||
|  | ||||
| 	void updateTerrainViews(const MapRect & rect); | ||||
| 	ETerrainGroup::ETerrainGroup getTerrainGroup(ETerrainType terType) const; | ||||
| 	/// Validates the terrain view of the given position and with the given pattern. | ||||
| 	/// Validates the terrain view of the given position and with the given pattern. The first method wraps the | ||||
| 	/// second method to validate the terrain view with the given pattern in all four flip directions(horizontal, vertical). | ||||
| 	ValidationResult validateTerrainView(const int3 & pos, const TerrainViewPattern & pattern, int recDepth = 0) const; | ||||
| 	ValidationResult validateTerrainViewInner(const int3 & pos, const TerrainViewPattern & pattern, int recDepth = 0) const; | ||||
| 	/// Tests whether the given terrain type is a sand type. Sand types are: Water, Sand and Rock | ||||
| 	bool isSandType(ETerrainType terType) const; | ||||
| 	TerrainViewPattern getFlippedPattern(const TerrainViewPattern & pattern, int flip) const; | ||||
|   | ||||
| @@ -14,14 +14,15 @@ add_executable(vcmitest ${test_SRCS}) | ||||
| target_link_libraries(vcmitest vcmi ${Boost_LIBRARIES}) | ||||
| add_test(vcmitest vcmitest) | ||||
|  | ||||
| # Files to copy to the build directory after compilation | ||||
| # Files to copy to the build directory | ||||
| add_custom_target(vcmitestFiles ALL) | ||||
| set(vcmitest_FILES | ||||
| 		TerrainViewTest.h3m | ||||
| 		terrainViewMappings.json | ||||
| ) | ||||
|  | ||||
| foreach(file ${vcmitest_FILES}) | ||||
| 	add_custom_command(TARGET vcmitest POST_BUILD | ||||
| 	COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/${file}" $<TARGET_FILE_DIR:vcmitest> | ||||
| 		add_custom_command(TARGET vcmitestFiles POST_BUILD | ||||
| 				COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/${file}" ${CMAKE_CURRENT_BINARY_DIR} | ||||
| 	) | ||||
| endforeach() | ||||
|   | ||||
| @@ -26,7 +26,7 @@ | ||||
| 			"pattern" : "normal.s6" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"pos" : [ [ 21,20,1 ], [ 28,27,1 ] ], | ||||
| 			"pos" : [ [ 21,20,1 ], [ 28,27,0 ] ], | ||||
| 			"pattern" : "normal.m1" | ||||
| 		}, | ||||
| 		{ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user