瀏覽代碼

Added Filesystem class (filesystem and IO operations);
Added more functions to Utilities namespace;
Updated to Intel TBB 2020 Update 3;
Changed a pre-defined constant in Math file, that was interfering with the newer Intel TBB
Fixed warnings (C4228) in SceneLoader class

Paul A 5 年之前
父節點
當前提交
8d7c8dfa98

+ 5 - 0
.gitignore

@@ -16,3 +16,8 @@
 *.tif
 VC x64/
 VC/
+*.tlog
+*.idb
+Praxis3D/x64/Debug 64bit/Praxis3D.vcxproj.FileListAbsolute.txt
+*.res
+*.recipe

二進制
Builds/x64/Debug 64bit/tbb.dll


二進制
Builds/x64/Debug 64bit/tbb_debug.dll


二進制
Builds/x64/Release 64bit/tbb.dll


+ 6 - 1
Praxis3D/Praxis3D.vcxproj

@@ -192,6 +192,10 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <DisableSpecificWarnings>
+      </DisableSpecificWarnings>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
@@ -203,7 +207,7 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <SDLCheck>true</SDLCheck>
-      <PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;TBB_SUPPRESS_DEPRECATED_MESSAGES;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <LanguageStandard>stdcpp17</LanguageStandard>
     </ClCompile>
@@ -368,6 +372,7 @@
     <ClInclude Include="Source\ErrorCodes.h" />
     <ClInclude Include="Source\ErrorHandler.h" />
     <ClInclude Include="Source\ErrorHandlerLocator.h" />
+    <ClInclude Include="Source\Filesystem.h" />
     <ClInclude Include="Source\FinalPass.h" />
     <ClInclude Include="Source\Framebuffer.h" />
     <ClInclude Include="Source\GeometryBuffer.h" />

+ 3 - 0
Praxis3D/Praxis3D.vcxproj.filters

@@ -545,6 +545,9 @@
     <ClInclude Include="Source\LenseFlareCompositePass.h">
       <Filter>Renderer\Render Passes\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Source\Filesystem.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="Data\config.ini" />

+ 65 - 0
Praxis3D/Source/Filesystem.h

@@ -0,0 +1,65 @@
+#pragma once
+
+#include <cstdlib>
+#include <filesystem>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include "Utilities.h"
+
+// General file and file-system operations
+class Filesystem
+{
+public:
+	// Creates directory tree up to the (and including) the given directory. Required full path. Returns true if successful
+	static bool createDirectories(const std::string &p_dirPathAndName)
+	{
+		return std::filesystem::create_directories(p_dirPathAndName);
+	}
+	
+	// Copies a single file. Returns true if successful
+	static bool copyFile(const std::string &p_sourceFile, const std::string &p_destinationFile)
+	{
+		if(exists(p_destinationFile))
+			std::filesystem::remove(p_destinationFile);
+		return std::filesystem::copy_file(p_sourceFile, p_destinationFile);
+	}
+
+	// Creates a destination directory tree and copies a single file
+	static bool copyFileCreateDirectory(const std::string &p_sourceFile, const std::string &p_destinationFile)
+	{
+		// Create the directory while also stripping down the file path
+		if(createDirectories(Utilities::stripFilePath(p_destinationFile)))
+		{
+			return copyFile(p_sourceFile, p_destinationFile);
+		}
+
+		return false;
+	}
+
+	// Write text (string) to file. Returns true if successful, false if not
+	static bool writeToFile(const std::string &p_text, const std::string &p_filename)
+	{
+		// Open file stream
+		std::ofstream exportFile(p_filename);
+
+		// Check if file stream is open
+		if(exportFile.is_open())
+		{
+			// Write text to file
+			exportFile << p_text << std::endl;
+			return true;
+		}
+
+		// If this point is reached, write operation failed
+		return false;
+	}
+
+	// Checks if file or directory exists, returns true if it does
+	static bool exists(const std::string &p_file)
+	{
+		return std::filesystem::exists(p_file);
+	}
+};

+ 1 - 1
Praxis3D/Source/Math.h

@@ -4,7 +4,7 @@
 
 #include <cmath>
 
-#define E			2.71828182845904523536
+#define E_CONST		2.71828182845904523536
 #define LOG2E		1.44269504088896340736
 #define LOG10E		0.434294481903251827651
 #define LN2			0.693147180559945309417

+ 5 - 5
Praxis3D/Source/SceneLoader.cpp

@@ -21,7 +21,7 @@ ErrorCode SceneLoader::loadFromFile(const std::string &p_filename)
 	auto &systemProperties = loadedProperties.getPropertySet().getPropertySetByID(Properties::Systems);
 
 	// Iterate over each system property set
-	for(decltype(systemProperties.getNumPropertySets()) propIndex = 0, const propSize = systemProperties.getNumPropertySets(); propIndex < propSize; propIndex++)
+	for(decltype(systemProperties.getNumPropertySets()) propIndex = 0, propSize = systemProperties.getNumPropertySets(); propIndex < propSize; propIndex++)
 	{
 		// Iterate over all systems scenes
 		for(int sysIndex = 0; sysIndex < Systems::NumberOfSystems; sysIndex++)
@@ -37,7 +37,7 @@ ErrorCode SceneLoader::loadFromFile(const std::string &p_filename)
 				auto &objectProperties = systemProperties.getPropertySetUnsafe(propIndex).getPropertySetByID(Properties::Objects);
 
 				// Iterate over all object property sets
-				for(decltype(objectProperties.getNumPropertySets()) objIndex = 0, const objSize = objectProperties.getNumPropertySets(); objIndex < objSize; objIndex++)
+				for(decltype(objectProperties.getNumPropertySets()) objIndex = 0, objSize = objectProperties.getNumPropertySets(); objIndex < objSize; objIndex++)
 				{
 					// Create a new system object (by pasting the object property set)
 					auto *newObject = m_systemScenes[sysIndex]->createObject(objectProperties.getPropertySetUnsafe(objIndex));
@@ -53,7 +53,7 @@ ErrorCode SceneLoader::loadFromFile(const std::string &p_filename)
 	auto &objLinkProperties = loadedProperties.getPropertySet().getPropertySetByID(Properties::ObjectLinks);
 
 	// Iterate over all object link property sets
-	for(decltype(objLinkProperties.getNumPropertySets()) linkIndex = 0, const linkSize = objLinkProperties.getNumPropertySets(); linkIndex < linkSize; linkIndex++)
+	for(decltype(objLinkProperties.getNumPropertySets()) linkIndex = 0, linkSize = objLinkProperties.getNumPropertySets(); linkIndex < linkSize; linkIndex++)
 	{
 		// Get subject name
 		const auto &subjectName = objLinkProperties.getPropertySetUnsafe(linkIndex).getPropertyByID(Properties::Subject).getString();
@@ -66,13 +66,13 @@ ErrorCode SceneLoader::loadFromFile(const std::string &p_filename)
 			continue;
 
 		// Iterate over created objects and match subject's name
-		for(decltype(createdObjects.size()) subjIndex = 0, const subjSize = createdObjects.size(); subjIndex < subjSize; subjIndex++)
+		for(decltype(createdObjects.size()) subjIndex = 0, subjSize = createdObjects.size(); subjIndex < subjSize; subjIndex++)
 		{
 			// Compare subject name
 			if(createdObjects[subjIndex].first == subjectName)
 			{
 				// Iterate over created objects and match observer's name
-				for(decltype(createdObjects.size()) observIndex = 0, const observSize = createdObjects.size(); observIndex < observSize; observIndex++)
+				for(decltype(createdObjects.size()) observIndex = 0, observSize = createdObjects.size(); observIndex < observSize; observIndex++)
 				{
 					// Compare observer name
 					if(createdObjects[observIndex].first == observerName)

+ 139 - 15
Praxis3D/Source/Utilities.h

@@ -60,28 +60,133 @@ namespace Utilities
 		else
 			return stringstream_ret.str() + "f";
 	}
+
+	// Replaces a word with a different word in a given string
+	static std::string replace(const std::string &p_fullText, const std::string &p_wordToReplace, const std::string &p_wordReplaceWith)
+	{
+		std::string returnString = p_fullText;
+		
+		// Search for the given character set and do a replace
+		while(returnString.find(p_wordToReplace) != std::string::npos)
+		{
+			returnString.replace(returnString.find(p_wordToReplace), p_wordToReplace.size(), p_wordReplaceWith.c_str());
+			break;
+		}
+
+		return returnString;
+	}	
 	
+	// Replaces all matched words with a different word in a given string; given word cannot contain the word to be replaced
+	static std::string replaceAll(const std::string &p_fullText, const std::string &p_wordToReplace, const std::string &p_wordReplaceWith)
+	{
+		std::string returnString = p_fullText;
 
-	// Static functions to convert a few simple types to Scancodes.
-	// Safe - checks if value in bounds. No conversion from string should be provided
+		// Check if the replacement word does not contain the character set to be replaced, so it does not go into an infinite loop
+		while(p_wordReplaceWith.find(p_wordToReplace) != std::string::npos)
+		{
+			return returnString;
+		}
 
-	static Scancode toScancode(const int p_value)
+		// Search for the given character set and do a replace
+		while(returnString.find(p_wordToReplace) != std::string::npos)
+		{
+			returnString.replace(returnString.find(p_wordToReplace), p_wordToReplace.size(), p_wordReplaceWith.c_str());
+		}
+
+		return returnString;
+	}
+
+	// Strip and return the filename from a full file path in Windows environment
+	static std::string stripFilename(const std::string &p_fullPath)
 	{
-		// If the passed value is within enum range, static cast it to a scancode, if not, return invalid scancode
-		if(p_value > Scancode::Key_Invalid && p_value < Scancode::NumberOfScancodes)
-			return static_cast<Scancode>(p_value);
-		else
-			return Scancode::Key_Invalid;
+		std::string returnFilename;
+
+		// Make sure full path string is not empty
+		if(!p_fullPath.empty())
+		{
+			// Separate the directory from the scene name
+			for(decltype(p_fullPath.size()) i = p_fullPath.size() - 1; i > 0; i--)
+			{
+				// Find the start (or end of directory) of the filename string
+				if(p_fullPath[i] == '\\' || p_fullPath[i] == '/')
+				{
+					// Cut the filename only and return it
+					returnFilename = p_fullPath.substr(i + 1, p_fullPath.size());
+					break;
+				}
+			}
+		}
+
+		return returnFilename;
 	}
-	static Scancode toScancode(const float p_value)
+
+	// Strip and return the directory path from a full file path in Windows environment
+	static std::string stripFilePath(const std::string &p_fullPath)
 	{
-		// If the passed value is within enum range, static cast it to a scancode, if not, return invalid scancode
-		if(p_value > Scancode::Key_Invalid && p_value < Scancode::NumberOfScancodes)
-			return static_cast<Scancode>((int)p_value);
-		else
-			return Scancode::Key_Invalid;
+		std::string returnFilename;
+
+		// Make sure full path string is not empty
+		if(!p_fullPath.empty())
+		{
+			// Separate the directory from the scene name
+			for(decltype(p_fullPath.size()) i = p_fullPath.size() - 1; i > 0; i--)
+			{
+				// Find the start (or end of directory) of the filename string
+				if(p_fullPath[i] == '\\' || p_fullPath[i] == '/')
+				{
+					// Cut the path only and return it
+					returnFilename = p_fullPath.substr(0, i + 1);
+					break;
+				}
+			}
+		}
+
+		return returnFilename;
 	}
 
+	// Removes the extension of a filename (removes everything after the last "." (dot) in the string, if it is present)
+	static std::string removeExtension(const std::string &p_filename)
+	{
+		std::string returnFilename;
+		
+		// Make sure full path string is not empty
+		if(!p_filename.empty())
+		{
+			// Go over each character from end to start
+			for(decltype(p_filename.size()) i = p_filename.size() - 1; i > 0; i--)
+			{
+				// If it is a dot, meaning the last dot character in the string
+				if(p_filename[i] == '.')
+				{
+					// Remove everything after the dot (including the dot itself); break the "for" loop
+					returnFilename = p_filename.substr(0, i);
+					break;
+				}
+			}
+		}
+
+		return returnFilename;
+	}
+
+	// Converts all slashes present in the string to backslashes ( '/' -> '\' )
+	static std::string slashToBackslash(const std::string& p_textWithSlashes)
+	{
+		std::string returnText;
+			
+		// Make sure full path string is not empty
+		if(!p_textWithSlashes.empty())
+		{
+			returnText = p_textWithSlashes;
+
+			// Separate the directory from the scene name
+			for(decltype(returnText.size()) i = 0, size = returnText.size(); i < size; i++)
+			{
+				if(returnText[i] == '/')
+					returnText[i] = '\\';
+			}
+		}
+		return returnText;
+	}
 
 	// A very simplistic but fast hash key function, converts string into an unsigned int
 	static unsigned int getHashKey(const std::string &p_string)
@@ -94,7 +199,6 @@ namespace Utilities
 		return hash;
 	}
 
-
 	// Template std::pair comparator, only compares the first element
 	template <class T1, class T2, class Pred = std::less<T2>>
 	struct sort_pair_first 
@@ -116,4 +220,24 @@ namespace Utilities
 			return p(left.second, right.second);
 		}
 	};
+
+	// Static functions to convert a few simple types to Scancodes.
+	// Safe - checks if value in bounds. No conversion from string should be provided
+
+	static Scancode toScancode(const int p_value)
+	{
+		// If the passed value is within enum range, static cast it to a scancode, if not, return invalid scancode
+		if(p_value > Scancode::Key_Invalid && p_value < Scancode::NumberOfScancodes)
+			return static_cast<Scancode>(p_value);
+		else
+			return Scancode::Key_Invalid;
+	}
+	static Scancode toScancode(const float p_value)
+	{
+		// If the passed value is within enum range, static cast it to a scancode, if not, return invalid scancode
+		if(p_value > Scancode::Key_Invalid && p_value < Scancode::NumberOfScancodes)
+			return static_cast<Scancode>((int)p_value);
+		else
+			return Scancode::Key_Invalid;
+	}
 }