From da4e8fd1b3d6759e5b2f5e4d771d82b689f08c2e Mon Sep 17 00:00:00 2001 From: Matus Novak Date: Tue, 30 Jun 2015 17:01:48 +0200 Subject: [PATCH] v1.3 - Added emissive atlas export - Attachments (docked ships and turrets) can now be exported - Removed WxWidgets and replaced with original Win32 GUI - Added option to select texture size - Fixed crash when exporting BMP textures - Changed the program flow to recursive due to the attachments - Added global namespace - Removed variables in global scope - Updated FFW library --- README.md | 312 +++-- SM2OBJ.cbp | 123 +- release/user-input.json | 19 + resource.rc | 8 - resources/application.manifest | 17 + resources/icon.ico | Bin 0 -> 370070 bytes resources/resources.rc | 2 + source/blockConfig.h | 46 - source/blockConstructor.h | 16 - source/blockExtractor.h | 22 - source/blockVerticeData.h | 40 - source/chunkHeader.h | 16 - source/chunkLoader.h | 17 - source/chunkTempLoader.cpp | 26 - source/chunkTempLoader.h | 15 - source/config.cpp | 26 - source/config.h | 76 -- source/defaults.cpp | 38 + source/defaults.h | 39 + source/{ => exporter}/blockConfig.cpp | 62 +- source/exporter/blockConfig.h | 50 + source/{ => exporter}/blockConstructor.cpp | 77 +- source/exporter/blockConstructor.h | 19 + source/{ => exporter}/blockExtractor.cpp | 113 +- source/exporter/blockExtractor.h | 25 + source/{ => exporter}/blockVerticeData.cpp | 43 +- source/exporter/blockVerticeData.h | 43 + source/{ => exporter}/chunkHeader.cpp | 7 +- source/exporter/chunkHeader.h | 19 + source/{ => exporter}/chunkLoader.cpp | 16 +- source/exporter/chunkLoader.h | 20 + source/exporter/chunkTempLoader.cpp | 27 + source/exporter/chunkTempLoader.h | 18 + source/exporter/config.cpp | 20 + source/exporter/config.h | 45 + source/exporter/constants.h | 27 + source/exporter/exportBlueprint.cpp | 1147 +++++++++++++++++ source/exporter/exportBlueprint.h | 20 + source/exporter/loadMeta.cpp | 258 ++++ source/exporter/loadMeta.h | 16 + source/exporter/materialExport.cpp | 68 + source/exporter/materialExport.h | 25 + source/{ => exporter}/polygonFiltering.cpp | 51 +- source/exporter/polygonFiltering.h | 20 + source/exporter/structures.h | 76 ++ source/exporter/textureExport.cpp | 336 +++++ source/exporter/textureExport.h | 22 + source/main.cpp | 14 + source/mainExporter.cpp | 529 -------- source/mainExporter.h | 14 - source/materialExport.cpp | 86 -- source/materialExport.h | 23 - source/polygonFiltering.h | 17 - source/textureExport.cpp | 234 ---- source/textureExport.h | 18 - source/widgets.cpp | 126 ++ source/widgets.h | 50 + source/window.cpp | 632 +++++++++ source/window.h | 101 ++ third-party-libs/FragmentFramework/LICENSE | 22 - third-party-libs/FragmentFramework/README.md | 41 - .../FragmentFramework/include/ffw.h | 2 +- .../include/gl/renderUtilities.h | 34 +- .../include/graphics/bufferObject.h | 18 +- .../include/graphics/graphicsShader.h | 95 +- .../include/graphics/graphicsTextureBase.h | 42 +- .../include/graphics/shaderBoxData.h | 32 + .../include/math/baseFunctions.h | 2 +- .../include/math/baseFunctions.inl | 2 +- .../FragmentFramework/include/math/mat4.h | 48 +- .../FragmentFramework/include/math/mat4.inl | 137 ++ .../FragmentFramework/include/math/math.h | 47 +- .../include/math/quaternion.h | 102 +- .../include/math/quaternion.inl | 63 +- .../include/math/typesOperators.h | 4 +- .../include/math/typesOperators.inl | 13 +- .../FragmentFramework/include/math/vec2.h | 192 +-- .../FragmentFramework/include/math/vec2.inl | 7 + .../FragmentFramework/include/math/vec3.h | 236 ++-- .../FragmentFramework/include/math/vec3.inl | 40 + .../FragmentFramework/include/math/vec4.h | 184 +-- .../FragmentFramework/include/math/vec4.inl | 41 + .../{listDirectory.h => directory.h} | 19 +- .../include/systemUtils/win32SysUtils.h | 4 +- .../include/utilities/loadSaveJson.cpp | 37 +- .../include/utilities/serialization.cpp | 358 ++++- .../include/utilities/serialization.h | 2 +- .../lib/{ => mingw}/FragmentFramework.dll | Bin 3382272 -> 3420160 bytes .../FragmentFramework/version.txt | 1 - widgets/SM2OBJApp - Copy.cpp | 24 - widgets/SM2OBJApp - Copy.h | 12 - widgets/SM2OBJApp.cpp | 24 - widgets/SM2OBJApp.h | 12 - widgets/SM2OBJMain - Copy.cpp | 260 ---- widgets/SM2OBJMain - Copy.h | 114 -- widgets/SM2OBJMain.cpp | 292 ----- widgets/SM2OBJMain.h | 122 -- wxsmith/SM2OBJframe.wxs | 189 --- 98 files changed, 5025 insertions(+), 3221 deletions(-) create mode 100644 release/user-input.json delete mode 100644 resource.rc create mode 100644 resources/application.manifest create mode 100644 resources/icon.ico create mode 100644 resources/resources.rc delete mode 100644 source/blockConfig.h delete mode 100644 source/blockConstructor.h delete mode 100644 source/blockExtractor.h delete mode 100644 source/blockVerticeData.h delete mode 100644 source/chunkHeader.h delete mode 100644 source/chunkLoader.h delete mode 100644 source/chunkTempLoader.cpp delete mode 100644 source/chunkTempLoader.h delete mode 100644 source/config.cpp delete mode 100644 source/config.h create mode 100644 source/defaults.cpp create mode 100644 source/defaults.h rename source/{ => exporter}/blockConfig.cpp (75%) create mode 100644 source/exporter/blockConfig.h rename source/{ => exporter}/blockConstructor.cpp (75%) create mode 100644 source/exporter/blockConstructor.h rename source/{ => exporter}/blockExtractor.cpp (52%) create mode 100644 source/exporter/blockExtractor.h rename source/{ => exporter}/blockVerticeData.cpp (82%) create mode 100644 source/exporter/blockVerticeData.h rename source/{ => exporter}/chunkHeader.cpp (88%) create mode 100644 source/exporter/chunkHeader.h rename source/{ => exporter}/chunkLoader.cpp (82%) create mode 100644 source/exporter/chunkLoader.h create mode 100644 source/exporter/chunkTempLoader.cpp create mode 100644 source/exporter/chunkTempLoader.h create mode 100644 source/exporter/config.cpp create mode 100644 source/exporter/config.h create mode 100644 source/exporter/constants.h create mode 100644 source/exporter/exportBlueprint.cpp create mode 100644 source/exporter/exportBlueprint.h create mode 100644 source/exporter/loadMeta.cpp create mode 100644 source/exporter/loadMeta.h create mode 100644 source/exporter/materialExport.cpp create mode 100644 source/exporter/materialExport.h rename source/{ => exporter}/polygonFiltering.cpp (72%) create mode 100644 source/exporter/polygonFiltering.h create mode 100644 source/exporter/structures.h create mode 100644 source/exporter/textureExport.cpp create mode 100644 source/exporter/textureExport.h create mode 100644 source/main.cpp delete mode 100644 source/mainExporter.cpp delete mode 100644 source/mainExporter.h delete mode 100644 source/materialExport.cpp delete mode 100644 source/materialExport.h delete mode 100644 source/polygonFiltering.h delete mode 100644 source/textureExport.cpp delete mode 100644 source/textureExport.h create mode 100644 source/widgets.cpp create mode 100644 source/widgets.h create mode 100644 source/window.cpp create mode 100644 source/window.h delete mode 100644 third-party-libs/FragmentFramework/LICENSE delete mode 100644 third-party-libs/FragmentFramework/README.md create mode 100644 third-party-libs/FragmentFramework/include/graphics/shaderBoxData.h rename third-party-libs/FragmentFramework/include/systemUtils/{listDirectory.h => directory.h} (68%) rename third-party-libs/FragmentFramework/lib/{ => mingw}/FragmentFramework.dll (75%) delete mode 100644 third-party-libs/FragmentFramework/version.txt delete mode 100644 widgets/SM2OBJApp - Copy.cpp delete mode 100644 widgets/SM2OBJApp - Copy.h delete mode 100644 widgets/SM2OBJApp.cpp delete mode 100644 widgets/SM2OBJApp.h delete mode 100644 widgets/SM2OBJMain - Copy.cpp delete mode 100644 widgets/SM2OBJMain - Copy.h delete mode 100644 widgets/SM2OBJMain.cpp delete mode 100644 widgets/SM2OBJMain.h delete mode 100644 wxsmith/SM2OBJframe.wxs diff --git a/README.md b/README.md index d6038d7..c5fd8fb 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,232 @@ # SM2OBJ - StarMade Blueprint to OBJ exporter -This program will exports blueprints from StarMade into an OBJ file. Texture export is also available in four formats (PNG, BMP, TIFF and TGA). This will load block config and textures from your StarMade installation. See "How to export blueprints" below. +This program will exports blueprints from StarMade into the OBJ file alongside with UV maps and textures (in four available formats: PNG, BMP, TGA, TIFF). The latest version of block configuration and textures are used to export the blueprint, these files are extracted directly from StarMade installation folder. **Check out main thread at StarMadeDock.net [here](http://starmadedock.net/threads/i-made-a-obj-exporter-for-blueprints-now-you-can-import-your-ships-to-any-3d-modeling-program.7994/)** -## This software uses following libraries: +## This software uses following libraries -* [FragmentFramework](http://matusnovak.github.io/fragmentframework/index.html) - A framework and a wrapper for several libraries listed in [FFW home page](http://matusnovak.github.io/fragmentframework/index.html) It is used as a main library and it does most of the work. -* [Zlib](http://www.zlib.net/) - Data decompression, required by loading chunks. -* [WxWidgets](https://www.wxwidgets.org/) - GUI for easier exporting +* [FragmentFramework](http://matusnovak.github.io/fragmentframework/index.html) - A framework and a wrapper for several libraries listed in [FFW home page](http://matusnovak.github.io/fragmentframework/index.html) +* [Zlib](http://www.zlib.net/) - Data decompression, required to decompress chunks. -## How to export blueprints with the GUI +## How to export the blueprint -This exporter comes with a GUI for easier use. There is a binary release [here at github](https://github.com/matusnovak/SM2OBJ/releases). If you want only the exporter itself, see code example below. +This exporter comes with a GUI for easier use. The binary release can be found [here at github](https://github.com/matusnovak/SM2OBJ/releases). If you want only the exporter itself, see the code example below. -![GUI Screenshot](http://i.imgur.com/GFvtaO6.jpg) +![GUI Screenshot](http://i.imgur.com/rvtr1BR.png) -Before you continue, make sure you have at least 500 MB of empty HDD space. This space is used only for temporary uses. The target OBJ file will be much smaller. If you are exporting extremely large structures, the used temporary space can be up to 2+GB. +Before you continue, make sure that you have at least 500 MB of empty HDD space. This space is used only for temporary use. The target OBJ file will be much smaller. If you are exporting extremely large structures, the used space can rise up to 2 GB. - 1. Select your blueprint by navigating to the StarMade folder. Remember to select the file which has "0.0.0" at the end of the file name. - 2. Select the "data" folder of your StarMade. This folder must contain both "config" and "textures" sub folders. Block IDs and textures will be extracted from this folder. - 3. Select the output path. Make sure that the output folder does exist. Both textures (inside folder named "textures") and *.obj file with *.mtl file will be saved here. - 4. Write the name of the output file. The same name will be used for the material file. - 5. Check "export materials" if you want to export the materials. You can also select which textures you wish to be exported. - 6. Select how many threads this program should use. If you have 4 core CPU, use 4 threads and so on... - 7. Leave the "Export UVs" option enabled. Disabling this will result in no UV maps, you will have to generate them yourself. Splitting tiles will split the atlas into separated image files. This will also create one material per generated image. Disabable this if you want to use the atlas instead. All atlases will be created alongside with the .obj file in "atlases" folder. - 8. Enable this if you want to export the textures. This needs to be done at least once. - 9. Select the texture format of exported textures/atlases. - 10. Happy exporting! +1. Select your input blueprint folder. Make sure the folder is not renamed and the original name is used (the name you used while saving the blueprint in-game) (Letter case must match!). + +2. Select the "data" folder of your StarMade. This folder must contain both "config" and "textures" sub folders. Block IDs and textures will be extracted from here. + +3. Select the output path. Make sure that the output folder does exist. + +4. UV maps can be exported in three ways: exported as tiles (creates one material per one tile) or exported as atlas (creates one material per one atlas) or not being exported at all. It is recommended to use an atlas if the blueprint is going to be used in Unreal Engine, Unity 3D or any other game engine. + +5. Disabling this option (not recommended) will lower the output side of the OBJ and increases import speed. + +6. Select which textures should all materials use (will affect both tiles and atlases). + +7. This affects the spectacular (shininess) of all materials. (This may not affect the materials at all in certain applications). + +8. Write a number of how many threads this export should use. (Using more threads than the number of cores your CPU has can result in lower performance). + +9. Select this option if you want your attachments (ships and turrets) to be exported alongside with the OBJ file. Note that objects are NOT merged together. After importing the OBJ into your favorite 3D editor, all attachments will be separated and located at [0,0,0] This ensures that the pivot of all ships and turrets is the axis of the rotation. However, the center of the mothership is located at the ship's core. This is due to the limitations of the OBJ file. + +10. Select the output folder for the textures. Make sure that the folder is valid, otherwise an error will appear. + +11. Output format of the textures. PNG is recommended due to compression. Note that changing this option will affect the exported materials. + +12. This option needs to match option number 4. + +13. Select the size of the tiles. If the textures are being exported as atlases, this will affect the export. (64px tile = 1024px atlas; 128px tile = 2048px atlas; 256px tile = 4096px atlas). + +14. Normals can be converted into bump maps, or not. Use the original normal maps if you are importing the OBJ in the game engine. ## How to export blueprints using source code only ```C++ -#include "source/mainExporter.h" +#include "source/exporter/textureExport.h" +#include "source/exporter/exportBlueprint.h" + +void sm2obj::window::callbackExportExit(bool Success){ + // Success <- true if the exporter finished without error +} + +void sm2obj::window::callbackExportProgress(int Progress, int Total){ + // Progress <- current progress + // Total <- progress out of maximum +} + +void sm2obj::window::callbackExportLogDebug(const std::string& Message){ + // Debug message +} + +void sm2obj::window::callbackExportLogError(const std::string& Message){ + // Error message + // After this error, the exportExitFunc function will + // be automatically called with "false" as argument. +} + +void sm2obj::window::callbackExportLogWarning(const std::string& Message){ + // Warning message + // If this function is called, the exported OBJ might not be + // valid! Always check for warning messages! +} + +void sm2obj::window::callbackExportLogInfo(const std::string& Message){ + // Info message + // This serves only for information about current progress +} + int main(){ - // Always make sure you set these variables! - // Leaving them undefined may crash the program - // Set texture export - textureExport = true; - // Should the textures be split into multiple - // files or use an atlas? - // Set false to use atlases - textureSplit = true; - // Set materials export - materialExport = true; - // Number of threads to use - threadsCount = 4; - // Path to the blueprint file - filePath = "C:\\StarMade\\blueprints\\..."; - // Path to StarMade data folder - // Make sure you won't add \ at the end! - starMadeDataFolder = "C:\\StarMade\\data"; - // Where to create an OBJ file - fileOutputFolder = "C:\\output"; - // Name of an OBJ file - fileName = "blueprint"; - // Export all textures - exportDiffuse = true; - exportBump = true; - exportAlpha = true; - // Use specular highlight - // Disable this if you do not want shiny materials - useSpecularHighlight = false; - // Export UV maps - uvsExport = true; - // Select texture format - imageSaver = &ffw::savePNG; - imageExtension = "png"; - // Or use one of these: - // imageSaver = &ffw::saveBMP; - // imageExtension = "bmp"; - // imageSaver = &ffw::saveTGA; - // imageExtension = "tga"; - // imageSaver = &ffw::saveTIFF; - // imageExtension = "tiff"; + // First, you need to set the callbacks for log messages + // Member non-static functions are not allowed! + sm2obj::config::exportLogErrorFunc = &callbackExportLogError; + sm2obj::config::exportLogDebugFunc = &callbackExportLogDebug; + sm2obj::config::exportLogInfoFunc = &callbackExportLogInfo; + sm2obj::config::exportLogWarningFunc = &callbackExportLogWarning; + sm2obj::config::exportExitFunc = &callbackExportExit; + sm2obj::config::exportProgressFunc = &callbackExportProgress; + + // Exporting the blueprint + + // Input and output folders (UTF-8 compatible) + std::string blueprintFolder + = "C:\\Program Files(x86)\\StarMade\\Blueprints\\..."; + std::string starMadeConfigFolder + = "C:\\Program Files(x86)\\StarMade\\data\\config"; + std::string outputFolder = "C:\\output"; + + // Output name without file extension + std::string outputName = "Blueprint"; + + // Threads + int numOfThreads = 8; + + // Set the texture extension + config::imageExtension = "png"; + + // Material options (see options #4, #5, #6 and #7 in GUI screenshot) + bool exportMaterials = true; + bool exportDiffuse = true; + bool exportAlpha = true; + bool exportNormal = true; + bool exportEmissive = true; + bool specularHighloght = false; + bool exportUvs = true; + bool useAtlas = false; // If true, the atlases will be used + + // Attachments options + bool attachments = true; + + // Start the exported by running this function + // You can run this function multiple times in parallel + // with different blueprints. + // However, variables that are set by sm2obj::config:: + // cannot be changed! + // The return value is either 0 (error) or 1 (success) + // The return type is void* + sm2obj::exportBlueprint(starMadeConfigFolder, blueprintFolder, + outputFolder, outputName, exportUvs, useAtlas, + exportMaterials, exportDiffuse, exportAlpha, + exportNormal, exportEmissive, specularHighloght, + numOfThreads, attachments); + + // Exporting textures + + // Set the texture function + // Can be either ffw::savePNG, ffw::saveBMP, + // ffw::saveTGA, ffw::saveTIFF or ffw::savePBM + sm2obj::config::imageSaverFunc = &ffw::savePNG; + // Set file extention + sm2obj::config::imageExtension = "png"; + + // Input and output folders (UTF-8 compatible) + std::string inputFolder + = "C:\\Program Files(x86)\\StarMade\\data" + + "\\textures\\block\\Default\\256"; + std::string starMadeConfigFolder + = "C:\\Program Files(x86)\\StarMade\\data\\config"; + std::string outputFolder = "C:\\output\\textures"; + + // Texture size + // This needs to match variable "inputFolder" + int textureSize = 256; + + // Convert normal maps to bump maps + // If false, original normals will be used + bool normals = false; + + // Export as separated tiles + exportTextures(inputFolder, outputFolder, normals); + + // Or export as atlases + //exportAtlases(inputFolder, outputFolder, normals); + //exportEmissiveAtlas(starMadeConfigFolder, outputFolder, textureSize); - // Run the exporter - runExporter(NULL); return 0; } ``` +## Error messages + + * __Failed to read block types from: ...__ + * __Failed to read block config from: ...__ + * The block configuration file might be missing, invalid or the data path is not correct. + * __Failed to load processed raw chunk data! Chunk index: ...__ + * __Failed to save processed raw chunk data! Chunk index: ...__ + * __Failed to save chunk vertices! Chunk index: ...__ + * __Failed to save chunk indices! Chunk index: ...__ + * __Error, failed to open vertex temp data! Index: "__ + * + * The chunk has not beed saved to the temp folder. The program might not have permissions to create/modify files or you are low on HDD space. + * __Error while reading chunk header!__ + * The blueprint might be corrupted! + * __Failed to open OBJ file for writing!__ + * __Failed to open MTL file for writing!__ + * The target output folder is invalid, you are low on HDD space or the program does not have permissions to create/modify files + * __Failed to open folder: ...__ + * The folder does not exists. + * __Error while retrieving file position from file name!__ + * It is necessary that all smd2 files are in this format: "Blueprint name" + "0.0.0.smd2" The last three digits indicated coordinate offset. + * __Failed to open file:__ + * The file does not exists or the program might not have permissions to open files. + * __File is invalid! File is too small. Expected at least 49156 bytes__ + * This is the minimal size of empty smd2 file which contains only chunk indices. The file might be corrupted if the size is lower. + * __Failed to open meta file from: ...__ + * The meta file is missing or the program does not have permission to do it. + * __Failed to read next attachment path from meta: ...__ + * Either you are trying to export old blueprint, or the meta file is corrupted. + * __Failed to save texture: ...__ + * The output folder of the textures is invalid. + * __Failed to load texture: ...__ + * The StarMade data folder does not contains textures or the program might not have permission to read files. + +## Warning messages + * __Error reading type ID / type name / icon / name / texture / type / light source / block style at line: ...__ + * The block config is invalid. + * __Could not find ID for type: ...__ + * The block type ID listed in BlockConfig.xml is missing. The block inside the XML file should be listed as: <Block icon="530" name="Grey Hull" textureId="33, 33, 33, 33, 33, 33" type="Grey_Hull"> If not, the warning message is generated. + * __Can not find block ID: ...__ + * There is a block that does not have a type ID listed in BlockTypes.properties file. + * __Chunk error while reading chunk header! Wrong relative position!__ + * The chunk is corrupted. This only affects one 16x16x16 chunk, not whole file. + * __Chunk failed to load! Z-Lib init error!__ + * This is internal zlib library error. There is not answer for this warning. + * __Can not decompress data! Chunk might be corrupted!__ + * This message appears if the chunk does not have 12288 bytes after decompression. + * __Error while saving chunk to temp!__ + * __Error loading chunk file index: ...__ + * The chunk has not beed saved to the temp folder. The program might not have permissions to create/modify files or you are low on HDD space. + + ## How to import exported OBJ to Blender Import the OBJ as usual and make sure you check "search for textures" in import options. Materials should appear alongside with textures. If not, copy .obj and .mtl file to "texture" folder (which was created if you checked "export textures") and import it again. -Blender will not load properly alpha textures. (This is Blender issue) You will need to fix them manualy. Also, self illuminated blocks does not import properly too. +Blender will not load properly alpha textures. (This is Blender issue) You will need to fix them manualy. Also, self illuminated blocks does not import properly. ## How to import exported OBJ to 3Ds Max @@ -92,9 +234,13 @@ When importing, select "Blender" preset from drop-down list, check "Import mater It may tell you that textures have not been found. It will give you an option to add a search patch. Point it to the exported texture folder. +## How to import exported OBJ to Unreal Engine + +I highly recommend to use atlases instead of tiles due to the performance. Also, you will need to import the OBJ into any other 3D modeling software and export it to the FBX format. Lightmap UV maps are not generated by SM2OBJ, you will to create them yourself (or do not use lightmaps at all, and use dynamic lightning only). + ## Performance -The export should not use more than 10MB of RAM (yes, mega bytes, not jiga bytes). If you use more threads, the RAM usage will rise by ~5MB per thread. +The export should not use more than 10MB of RAM. If you use more threads, the RAM usage will rise by ~5MB per thread. ## Bugs @@ -104,45 +250,51 @@ The export should not use more than 10MB of RAM (yes, mega bytes, not jiga bytes After downloading the source code, you will get these files and folders: -* **source/** - Source code for the exporter, no GUI files here. +* **source/** - GUI window classes. +* **source/exported** - Source files of the exporter only. * **third-party-libs/** - Contains [FragmentFramework](http://matusnovak.github.io/fragmentframework/index.html) and [Zlib](http://www.zlib.net/) (both include headers and binaries) -* **widgets/** - Source code for GUI, more precisely the class for the frame window. -* **wxsmith/** - XML file used by WxSmith -* **LICENSE** - License file +* **resources/** - Resource file and application icon. +* **LICENSE** - License file. * **README.md** - You are reading it now. -* **SM2OBJ.cbp** - Code::Blocks project file -* **resource.rc** - Required by the GUI +* **SM2OBJ.cbp** - Code::Blocks project file. -**Source files of the exporter inside the source/ folder:** +**Source files of the exporter inside the source/exporter folder:** -* **mainExporter.cpp** - Main program flow. * **blockConfig.cpp** - Loads block config from StarMade data folder. * **blockConstructor.cpp** - Constructs blocks vertices and indices. * **blockExtractor.cpp** - Exports vertices and indices as a text to temporary files. * **blockVerticesData.cpp** - Contains blocks vertex, face and UV data exported from OBJ. (block, wdge, tetra, penta and corner). +* **config.cpp** - Configuration class. +* **constants.cpp** - Constants. Nothing more to say about this file. +* **exportBlueprint.cpp** - Main program flow. * **chunkHeader.cpp** - Loads a header from smd2 file. * **chunkLoader.cpp** - Loads and decompress chunk data. * **chunkTempLoader.cpp** - Used for temporary saving decompressed chunks. -* **config.cpp** - Config file, here are the variables that are used in the code example above. +* **loadMeta.cpp** - Loads meta file (meta.smbpm). * **materialExporter.cpp** - Exports material to the mtl file. * **polygonFiltering.cpp** - Filters polygons (ones that occupies same space and removes unnecessary vertices). +* **structures.h** - All main data structures used by whole program. * **textureExport.cpp** - Exports and converts textures. ## How to compile: The binary releases are compiled with MinGW-w64 i686 GCC 4.9.2 I suggest that you use the same compiler. -Use Code::Blocks project `SM2OBJ.cbp` file or: +Prefer to use Code::Blocks project file `SM2OBJ.cbp` -To compile without the GUI, include all source files in the `source` folder and you also need to provide compiler search paths to these folders: -* `third-party-libs\FragmentFramework\include` -* `third-party-libs\zlib\include` +Or, to compile with command line: Include all source files in the `source` and `source/exporter` folders. Add compile search path to third party libraries located in `third-party-libs\FragmentFramework\include` and `third-party-libs\zlib\include` No other libraries are needed. Only `FragmentFramework.dll` is needed to be linked with the exe file (zlib is already linked in this DLL). -Next, link `FragmentFramework` DLL located inside the `third-party-libs\FragmentFramework\lib` folder. (Zlib 1.2.8 is already included inside the FragmentFramework.dll) +If you are using Visual Studio, check [FragmentFramework](https://github.com/matusnovak/fragmentframework) repository for the VS 2013 version of the library in: /FragmentFramework/lib/msvc/. -Note that you will need to download and compile the WxWidgets yourself. The library is not distributed with this project! Does not apply if you exclude the GUI from source. +## ToDo -If you are using Visual Studio, check [FragmentFramework](https://github.com/matusnovak/fragmentframework) repository for the VS 2013 version of the library in: /FragmentFramework/lib/msvc/. +* Add comments to the source files + +## Please comment + +If bug occurs or you have problems please report the issue. Use github's issue tracker. + +If you have questions or suggestions please feel free to contact me at GitHub or at at my email address. ## Copyright and license diff --git a/SM2OBJ.cbp b/SM2OBJ.cbp index 5c41b30..edc11d4 100644 --- a/SM2OBJ.cbp +++ b/SM2OBJ.cbp @@ -8,125 +8,92 @@ - - - - - - - - - - - - - - - - - - - - - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - diff --git a/release/user-input.json b/release/user-input.json new file mode 100644 index 0000000..d3c0d08 --- /dev/null +++ b/release/user-input.json @@ -0,0 +1,19 @@ +{ + "export-attachments": true, + "exportMaterials": true, + "input-blueprint-folder": "C:\\Program Files (x86)\\StarMade\\blueprints\\Isanth Type-Zero Mb", + "input-data-folder": "C:\\Program Files (x86)\\data", + "num-of-threads": "8", + "output-file-folder": "C:\\output", + "output-texture-folder": "C:\\output\\textures", + "specular-highlight": false, + "texture-export-type": 0, + "texture-normals": 1, + "texture-output-format": 0, + "texture-tile-size": 2, + "use-alpha-textures": true, + "use-diffuse-textures": true, + "use-emissive-textures": true, + "use-normal-textures": true, + "uv-maps-options": 1 +} \ No newline at end of file diff --git a/resource.rc b/resource.rc deleted file mode 100644 index d7d0c65..0000000 --- a/resource.rc +++ /dev/null @@ -1,8 +0,0 @@ -aaaa ICON "wx/msw/std.ico" - - - -#include "wx/msw/wx.rc" - - - diff --git a/resources/application.manifest b/resources/application.manifest new file mode 100644 index 0000000..1329711 --- /dev/null +++ b/resources/application.manifest @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/icon.ico b/resources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3eb1f8b1a555b4bb59913440965ad9ee817b7ae0 GIT binary patch literal 370070 zcmeEv4{%k*y?;Uyk`O`&A%qY@5<&&CEAv&z{}ye*f^<-tc&Q%2y8MU(?lt^dFQ zy!xLIB2@yuH17czSGTQ<7! z->xF=%;3J^0p<^-rE>c-Y8q92zj_wqrG8 zzjFn6>k$88zrx7}bMxvNYQ}!|?Y;#Q7wY+Qa;xW#tpDD7jIXTOun*;(7(4ODS1U)X ze|z|-M=kE-M1U;N@1=iG6} z9oxS7&2R2rzI^#I{d>6g{rmSnKXc~HQ*CW+`yYA4;y)u?9`*YVfB3`qZ@cZbcXsU9 z@$Tl$o8MNyd4)66d(>1F=FGY4uDkZ+=jYE&O-We-Srqk1wQMI3&ncnu- h$-mrD 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 literal 0 HcmV?d00001 diff --git a/resources/resources.rc b/resources/resources.rc new file mode 100644 index 0000000..25ee97c --- /dev/null +++ b/resources/resources.rc @@ -0,0 +1,2 @@ +1 24 "resources/application.manifest" +11 ICON "resources/icon.ico" diff --git a/source/blockConfig.h b/source/blockConfig.h deleted file mode 100644 index 47116b4..0000000 --- a/source/blockConfig.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_BLOCK_CONFIG -#define SMD2_EXPORTER_BLOCK_CONFIG - -#include - -// Block info -struct blockInfoStruct { - // Block name - std::string name; - // Block icon (not used) - int icon; - // Block texture ID - // In this order: Front, Back, Top, Bottom, Left, Right - uint16_t texture[6]; - // Block ID - uint16_t id; - // Is block transparent? - bool transparent; - // Is block emissive? - bool emissive; - // Light color - ffw::color light; - // Type of object - // -1 = undefined - // 0 = cube - // 1 = wedge - // 2 = corner - // 3 = X-shape - // 4 = tetra - // 5 = hepta - int object; -}; - -bool loadBlockTypes(const std::string& Path); -bool loadBlockConfig(const std::string& Path); -const blockInfoStruct* findBlock(int ID); -const blockInfoStruct* findBlockByTexture(int Index); - -#endif - - diff --git a/source/blockConstructor.h b/source/blockConstructor.h deleted file mode 100644 index efb37a1..0000000 --- a/source/blockConstructor.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_BLOCK_CONSTRUCTOR -#define SMD2_EXPORTER_BLOCK_CONSTRUCTOR - -#include -#include "config.h" - -bool buildBlock(ffw::vec3i Pos, ffw::vec3i PosRel, ffw::vec3i PosFile, uint32_t ChunkData[][16][16], chunkBufferStruct* Output); - -#endif - - diff --git a/source/blockExtractor.h b/source/blockExtractor.h deleted file mode 100644 index 1e565c0..0000000 --- a/source/blockExtractor.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_BLOCK_EXTRACTOR -#define SMD2_EXPORTER_BLOCK_EXTRACTOR - -#include -#include "config.h" - -void resetIndiceOffset(); -void resetMaterials(); -const std::vector& getExtractedTiles(); -bool saveRawBlocks(const std::string& ExecutablePath, chunkBufferStruct* Input, int ChunkIndex, int FileIndex); -bool loadRawBlocks(const std::string& ExecutablePath, chunkBufferStruct* Input, int ChunkIndex, int FileIndex); -bool extractBlocks(const std::string& ExecutablePath, chunkBufferStruct* Input, int ChunkIndex, int FileIndex); - -#endif - - - diff --git a/source/blockVerticeData.h b/source/blockVerticeData.h deleted file mode 100644 index d717fb2..0000000 --- a/source/blockVerticeData.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_BLOCK_VERTICE_DATA -#define SMD2_EXPORTER_BLOCK_VERTICE_DATA - -#include - -extern ffw::vec2f globalTextureUvs[4]; - -extern ffw::vec3f cubeVertices[8]; -extern ffw::vec4i cubeFaces[6]; -extern ffw::vec4i cubeFacesUvs[6]; - -extern ffw::vec3f wedgeVertices[6]; -extern ffw::vec4i wedgeFaces[5]; -extern ffw::vec4i wedgeFacesUvs[5]; - -extern ffw::vec3f heptaVertices[7]; -extern ffw::vec4i heptaFaces[7]; -extern ffw::vec4i heptaFacesUvs[7]; - -extern ffw::vec3f tetraVertices[4]; -extern ffw::vec4i tetraFaces[4]; -extern ffw::vec4i tetraFacesUvs[4]; - -extern ffw::vec3f cornerVertices[5]; -extern ffw::vec4i cornerFaces[5]; -extern ffw::vec4i cornerFacesUvs[5]; - -extern ffw::vec3f XshapeVertices[8]; -extern ffw::vec4i XshapeFaces[2]; -extern ffw::vec4i XshapeFacesUvs[2]; - -#endif - - - diff --git a/source/chunkHeader.h b/source/chunkHeader.h deleted file mode 100644 index e147899..0000000 --- a/source/chunkHeader.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_CHUNK_HEADER -#define SMD2_EXPORTER_CHUNK_HEADER - -#include - -bool loadChunkHeader(ffw::file* File, int* ChunkIndex, int* TotalChunks); - -#endif - - - diff --git a/source/chunkLoader.h b/source/chunkLoader.h deleted file mode 100644 index 56e8a48..0000000 --- a/source/chunkLoader.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_CHUNK_LOADER -#define SMD2_EXPORTER_CHUNK_LOADER - -#include - -bool loadChunk(ffw::file* File, size_t FileOffset, int ChunkIndex, uint32_t ChunkData[][16][16], ffw::vec3i* Pos); - -#endif - - - - diff --git a/source/chunkTempLoader.cpp b/source/chunkTempLoader.cpp deleted file mode 100644 index e79fad2..0000000 --- a/source/chunkTempLoader.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#include "chunkTempLoader.h" -#include "config.h" - -///============================================================================= -bool saveTempChunk(const std::string& ExecutablePath, int Index, int FileIndex, uint32_t* Buffer){ - ffw::file input; - if(!input.open(ExecutablePath + "\\temp\\"+ ffw::valToString(FileIndex) + "_" + ffw::valToString(Index) + ".data", true, true, true))return false; - if(!input.write(Buffer, CHUNK_SIZE*sizeof(uint32_t)))return false; - input.close(); - return true; -} - -///============================================================================= -bool loadTempChunk(const std::string& ExecutablePath, int Index, int FileIndex, uint32_t* Buffer){ - ffw::file input; - if(!input.open(ExecutablePath + "\\temp\\"+ ffw::valToString(FileIndex) + "_" + ffw::valToString(Index) + ".data", true, false, false))return false; - if(input.getSize() < CHUNK_SIZE*sizeof(uint32_t))return false; - if(!input.read(Buffer, CHUNK_SIZE*sizeof(uint32_t)))return false; - input.close(); - return true; -} diff --git a/source/chunkTempLoader.h b/source/chunkTempLoader.h deleted file mode 100644 index 1965077..0000000 --- a/source/chunkTempLoader.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_CHUNK_TEMP_LOADER -#define SMD2_EXPORTER_CHUNK_TEMP_LOADER - -#include - -bool saveTempChunk(const std::string& ExecutablePath, int Index, int FileIndex, uint32_t* Buffer); -bool loadTempChunk(const std::string& ExecutablePath, int Index, int FileIndex, uint32_t* Buffer); - -#endif - diff --git a/source/config.cpp b/source/config.cpp deleted file mode 100644 index fedf9e7..0000000 --- a/source/config.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#include "config.h" - -chunkBufferStruct chunkBufferA; -chunkBufferStruct chunkBufferB; -bool (*imageSaver)(const std::string& Path, unsigned char* Pixels, int Width, int Height, ffw::imageType Type); -std::string imageExtension; -bool textureExport = true; -bool textureSplit = false; -bool materialExport = true; -bool uvsExport = true; -bool useSpecularHighlight = false; -int threadsCount = 1; -std::string filePath; -bool exportDiffuse= true; -bool exportBump= true; -bool exportAlpha= true; -void (*exportProgressFunc)(int A, int B) = NULL; -void (*exportExitFunc)(bool Success) = NULL; -std::string fileOutputFolder; -std::string fileName; -std::string starMadeDataFolder; diff --git a/source/config.h b/source/config.h deleted file mode 100644 index f5e278e..0000000 --- a/source/config.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_CONFIG -#define SMD2_EXPORTER_CONFIG - -#include - -#define CHUNK_SIZE 16*16*16 -#define CHUNK_MAX_VERTICES 8 -#define CHUNK_MAX_INDICES 8 - -struct chunkBufferStruct { - ffw::vec3f vertices[CHUNK_SIZE * CHUNK_MAX_VERTICES]; - uint32_t verticesCount; - ffw::vec4i indices[CHUNK_SIZE * CHUNK_MAX_INDICES]; - ffw::vec4i indicesUvs[CHUNK_SIZE * CHUNK_MAX_INDICES]; - uint16_t indicesMat[CHUNK_SIZE * CHUNK_MAX_INDICES][3]; - uint32_t indicesCount; - uint32_t indicesOffset; -}; - -extern chunkBufferStruct chunkBufferA; -extern chunkBufferStruct chunkBufferB; -// Pointer to image saving function -// Use one of these: -// imageSaver = &ffw::saveBMP; -// imageSaver = &ffw::savePNG; -// imageSaver = &ffw::saveTGA; -// imageSaver = &ffw::saveTIFF; -extern bool (*imageSaver)(const std::string& Path, unsigned char* Pixels, int Width, int Height, ffw::imageType Type); -// Pointer to image saving function -// Use one of these: -// imageExtension = "bmp"; -// imageExtension = "png"; -// imageExtension = "tga"; -// imageExtension = "tiff"; -extern std::string imageExtension; -// Should textures be exported? -extern bool textureExport; -// Should textures be split? -extern bool textureSplit; -// Should materials be exported? -extern bool materialExport; -// Should UVs be exported? -extern bool uvsExport; -// Specular highlights on / off -extern bool useSpecularHighlight; -// Number of threads 0 - 16 -extern int threadsCount; -// Path to blueprint file, for example: -// C:\\StarMade\\blueprints\\Isanth Type-Zero Mb\\DATA\\Isanth Type-Zero Mb.0.0.0.smd2 -extern std::string filePath; -// Output folder -extern std::string fileOutputFolder; -// Output filename (without extension) -extern std::string fileName; -// Path to StarMade data folder, for example: -// C:\\StarMade\\data -extern std::string starMadeDataFolder; -// Should materials use diffuse texture? -extern bool exportDiffuse; -// Should materials use bump texture? -extern bool exportBump; -// Should materials use alpha texture? -extern bool exportAlpha; -// Callback when chunks are beeing exported -// A - current chunk -// B - total chunks -extern void (*exportProgressFunc)(int A, int B); -// Callback when export exists with Success or without -extern void (*exportExitFunc)(bool Success); - -#endif diff --git a/source/defaults.cpp b/source/defaults.cpp new file mode 100644 index 0000000..953c894 --- /dev/null +++ b/source/defaults.cpp @@ -0,0 +1,38 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include "defaults.h" + +sm2obj::defaults::defaults(){ + serialize(&inputBlueprintFolder, "input-blueprint-folder"); + serialize(&inputDataFolder, "input-data-folder"); + serialize(&outputFileFolder, "output-file-folder"); + serialize(&uvMapsOptions, "uv-maps-options"); + serialize(&exportMaterials, "exportMaterials"); + serialize(&useDiffuseTextures, "use-diffuse-textures"); + serialize(&useAlphaTextures, "use-alpha-textures"); + serialize(&useNormalTextures, "use-normal-textures"); + serialize(&useEmissiveTextures, "use-emissive-textures"); + serialize(&specularHighlight, "specular-highlight"); + serialize(&numOfThreads, "num-of-threads"); + serialize(&outputTextureFolder, "output-texture-folder"); + serialize(&textureOutputFormat, "texture-output-format"); + serialize(&textureExportType, "texture-export-type"); + serialize(&textureTileSize, "texture-tile-size"); + serialize(&textureNormals, "texture-normals"); + serialize(&exportAttachments, "export-attachments"); +} + +sm2obj::defaults::~defaults(){ +} + +bool sm2obj::defaults::load(const std::string& Path){ + return deserializeAsJson(Path, NULL); +} + +bool sm2obj::defaults::save(const std::string& Path){ + return serializeAsJson(Path, true); +} diff --git a/source/defaults.h b/source/defaults.h new file mode 100644 index 0000000..40cf75d --- /dev/null +++ b/source/defaults.h @@ -0,0 +1,39 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_DEFAULTS +#define SM2OBJ_DEFAULTS + +#include + +namespace sm2obj { + class defaults: public ffw::serialization { + public: + defaults(); + ~defaults(); + bool load(const std::string& Path); + bool save(const std::string& Path); + + std::string inputBlueprintFolder; + std::string inputDataFolder; + std::string outputFileFolder; + int uvMapsOptions; + bool exportMaterials; + bool useDiffuseTextures; + bool useAlphaTextures; + bool useNormalTextures; + bool useEmissiveTextures; + bool specularHighlight; + std::string numOfThreads; + std::string outputTextureFolder; + int textureOutputFormat; + int textureExportType; + int textureTileSize; + int textureNormals; + bool exportAttachments; + }; +}; +#endif diff --git a/source/blockConfig.cpp b/source/exporter/blockConfig.cpp similarity index 75% rename from source/blockConfig.cpp rename to source/exporter/blockConfig.cpp index 8537fa8..1d1db44 100644 --- a/source/blockConfig.cpp +++ b/source/exporter/blockConfig.cpp @@ -1,25 +1,27 @@ /* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License */ #include "blockConfig.h" +#include "config.h" struct blockTypeStruct { std::string name; int id; }; -std::vector blocksInfo; -std::vector blocksType; +static std::vector blocksInfo; +static std::vector blocksType; ///============================================================================= -void fixName(std::string* Str){ +static void fixName(std::string* Str){ for(auto& chr : *Str)if(chr == ' ')chr = '_'; } ///============================================================================= -std::string fixSpaces(const std::string& Str){ +static std::string fixSpaces(const std::string& Str){ if(Str.size() == 1)return Str; size_t first = 0; @@ -41,14 +43,15 @@ std::string fixSpaces(const std::string& Str){ } ///============================================================================= -bool loadBlockTypes(const std::string& Path){ +bool sm2obj::loadBlockTypes(const std::string& Path){ // Open file for input ffw::file input; if(!input.open(Path, false, false, false)){ + LOG_ERROR("Failed to read block types from: " + Path); return false; } - ffw::logger().debug() << "Reading block type properties from: " << Path; + LOG_INFO("Reading block type properties..."); int lineNumber = 0; while(!input.eof()){ @@ -68,12 +71,12 @@ bool loadBlockTypes(const std::string& Path){ id = ffw::stringToVal(tokens[1]); } catch (std::exception& e){ - ffw::logger().warning() << "Error reading type ID at line: " << lineNumber; + LOG_WARNING("Error reading type ID at line: " + ffw::valToString(lineNumber)); continue; } if(tokens[0].size() == 0){ - ffw::logger().warning() << "Error reading type name at line: " << lineNumber; + LOG_WARNING("Error reading type name at line: " + ffw::valToString(lineNumber)); continue; } @@ -81,12 +84,12 @@ bool loadBlockTypes(const std::string& Path){ } } - ffw::logger().debug() << "Loaded: " << blocksType.size() << " block types!"; + LOG_DEBUG("Loaded: " + ffw::valToString(blocksType.size()) + " block types!"); return true; } ///============================================================================= -int findBlockType(const std::string& Type){ +static int findBlockType(const std::string& Type){ for(const auto& block : blocksType){ if(block.name == Type)return block.id; } @@ -94,7 +97,7 @@ int findBlockType(const std::string& Type){ } ///============================================================================= -const blockInfoStruct* findBlock(int ID){ +const sm2obj::blockInfoStruct* sm2obj::findBlock(int ID){ for(const auto& block : blocksInfo){ if(block.id == ID)return █ } @@ -102,7 +105,7 @@ const blockInfoStruct* findBlock(int ID){ } ///============================================================================= -const blockInfoStruct* findBlockByTexture(int Index){ +const sm2obj::blockInfoStruct* sm2obj::findBlockByTexture(int Index){ for(const auto& block : blocksInfo){ for(int i = 0; i < 6; i++){ if(block.texture[i] == Index)return █ @@ -112,14 +115,21 @@ const blockInfoStruct* findBlockByTexture(int Index){ } ///============================================================================= -bool loadBlockConfig(const std::string& Path){ +void sm2obj::clearBlockConfig(){ + blocksInfo.clear(); + blocksType.clear(); +} + +///============================================================================= +bool sm2obj::loadBlockConfig(const std::string& Path){ // Open file for input ffw::file input; if(!input.open(Path, false, false, false)){ + LOG_ERROR("Failed to read block config from: " + Path); return false; } - ffw::logger().debug() << "Reading block config from: " << Path; + LOG_INFO("Reading block config..."); // Loop through all lines int lineNumber = 0; @@ -133,7 +143,7 @@ bool loadBlockConfig(const std::string& Path){ if(line.find("(tokens[1].substr(1, pos-1)); } catch (std::exception& e){ - ffw::logger().warning() << "Error reading icon data at line: " << lineNumber; + LOG_WARNING("Error reading icon data at line: " + ffw::valToString(lineNumber)); blocksInfo.pop_back(); continue; } @@ -155,7 +165,7 @@ bool loadBlockConfig(const std::string& Path){ if(pos != std::string::npos){ newBlock.name = tokens[2].substr(1, pos-1); if(newBlock.name.size() == 0){ - ffw::logger().warning() << "Error reading name data at line: " << lineNumber; + LOG_WARNING("Error reading name data at line: " + ffw::valToString(lineNumber)); blocksInfo.pop_back(); continue; } @@ -166,7 +176,7 @@ bool loadBlockConfig(const std::string& Path){ if(pos != std::string::npos){ std::vector texTokens = ffw::getTokens(tokens[3].substr(1, pos-1), ','); if(texTokens.size() != 6){ - ffw::logger().warning() << "Error reading texture data at line: " << lineNumber; + LOG_WARNING("Error reading texture data at line: " + ffw::valToString(lineNumber)); blocksInfo.pop_back(); continue; } @@ -177,7 +187,7 @@ bool loadBlockConfig(const std::string& Path){ } } catch (std::exception& e){ - ffw::logger().warning() << "Error reading texture data at line: " << lineNumber; + LOG_WARNING("Error reading texture data at line: " + ffw::valToString(lineNumber)); blocksInfo.pop_back(); continue; } @@ -187,14 +197,14 @@ bool loadBlockConfig(const std::string& Path){ if(pos != std::string::npos){ std::string type = tokens[4].substr(1, pos-1); if(type.size() == 0){ - ffw::logger().warning() << "Error reading type data at line: " << lineNumber; + LOG_WARNING("Error reading type data at line: " + ffw::valToString(lineNumber)); blocksInfo.pop_back(); continue; } int id = findBlockType(type); if(id == -1){ - ffw::logger().warning() << "Could not find ID for type: " << type; + LOG_WARNING("Could not find ID for type: " + type); continue; } @@ -227,7 +237,7 @@ bool loadBlockConfig(const std::string& Path){ // Nothing to do } } - if(!success)ffw::logger().warning() << "Error reading light source color data at line: " << lineNumber; + if(!success)LOG_WARNING("Error reading light source color data at line: " + ffw::valToString(lineNumber)); } } else if(blocksInfo.size() > 0 && (first = line.find("")) != std::string::npos){ @@ -242,11 +252,11 @@ bool loadBlockConfig(const std::string& Path){ // Nothing to do } } - if(!success)ffw::logger().warning() << "Error reading block style as int at line: " << lineNumber; + if(!success)LOG_WARNING("Error reading block style as int at line: " + ffw::valToString(lineNumber)); } } - ffw::logger().debug() << "Loaded: " << blocksType.size() << " blocks!"; + LOG_DEBUG("Loaded: " + ffw::valToString(blocksInfo.size()) + " blocks!"); return true; } diff --git a/source/exporter/blockConfig.h b/source/exporter/blockConfig.h new file mode 100644 index 0000000..a70b6c8 --- /dev/null +++ b/source/exporter/blockConfig.h @@ -0,0 +1,50 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SMD2OBJ_BLOCK_CONFIG +#define SMD2OBJ_BLOCK_CONFIG + +#include + +namespace sm2obj{ + // Block info + struct blockInfoStruct { + // Block name + std::string name; + // Block icon (not used) + int icon; + // Block texture ID + // In this order: Front, Back, Top, Bottom, Left, Right + uint16_t texture[6]; + // Block ID + uint16_t id; + // Is block transparent? + bool transparent; + // Is block emissive? + bool emissive; + // Light color + ffw::color light; + // Type of object + // -1 = undefined + // 0 = cube + // 1 = wedge + // 2 = corner + // 3 = X-shape + // 4 = tetra + // 5 = hepta + int object; + }; + + void clearBlockConfig(); + bool loadBlockTypes(const std::string& Path); + bool loadBlockConfig(const std::string& Path); + const blockInfoStruct* findBlock(int ID); + const blockInfoStruct* findBlockByTexture(int Index); +}; + +#endif + + diff --git a/source/blockConstructor.cpp b/source/exporter/blockConstructor.cpp similarity index 75% rename from source/blockConstructor.cpp rename to source/exporter/blockConstructor.cpp index fa28e59..3dd715d 100644 --- a/source/blockConstructor.cpp +++ b/source/exporter/blockConstructor.cpp @@ -1,18 +1,19 @@ /* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License */ #include "blockConstructor.h" #include "blockConfig.h" #include "blockVerticeData.h" -static void addObjectCube(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block); -static void addObjectWedge(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block); -static void addObjectHepta(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block); -static void addObjectXshape(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block); -static void addObjectTetra(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block); -static void addObjectCorner(chunkBufferStruct* Output, const ffw::vec3f& Pos, int RotA, int RotB, const blockInfoStruct* Block); +static void addObjectCube(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block); +static void addObjectWedge(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block); +static void addObjectHepta(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block); +static void addObjectXshape(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block); +static void addObjectTetra(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block); +static void addObjectCorner(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int RotA, int RotB, const sm2obj::blockInfoStruct* Block); ///============================================================================= static void getBlock(uint32_t Buffer[][16][16], int* BlockID, int* RotA, int* RotB, int PosX, int PosY, int PosZ){ @@ -31,7 +32,7 @@ static void getBlock(uint32_t Buffer[][16][16], int* BlockID, int* RotA, int* Ro } ///============================================================================= -bool buildBlock(ffw::vec3i Pos, ffw::vec3i PosRel, ffw::vec3i PosFile, uint32_t ChunkData[][16][16], chunkBufferStruct* Output){ +bool sm2obj::buildBlock(ffw::vec3i Pos, ffw::vec3i PosRel, ffw::vec3i PosFile, uint32_t ChunkData[][16][16], chunkBufferStruct* Output){ int id; int rotA; int rotB; @@ -46,6 +47,7 @@ bool buildBlock(ffw::vec3i Pos, ffw::vec3i PosRel, ffw::vec3i PosFile, uint32_t const blockInfoStruct* block = findBlock(id); if(block == NULL){ // Can not find block ID + LOG_WARNING("Can not find block ID: " + ffw::valToString(id)); } else { // -1 = undefined // 0 = cube @@ -62,6 +64,7 @@ bool buildBlock(ffw::vec3i Pos, ffw::vec3i PosRel, ffw::vec3i PosFile, uint32_t case 3: addObjectXshape(Output, PosF, rotA, block); break; case 4: addObjectTetra(Output, PosF, rotA, block); break; case 5: addObjectHepta(Output, PosF, rotA, block); break; + case 6: addObjectCube(Output, PosF, rotA, block); break; default: break; }; } @@ -70,11 +73,11 @@ bool buildBlock(ffw::vec3i Pos, ffw::vec3i PosRel, ffw::vec3i PosFile, uint32_t } ///============================================================================= -void addObjectCube(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block){ +void addObjectCube(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block){ // Add vertices for(uint32_t i = 0, p = Output->verticesCount; i < 8; i++, p++){ // Get vector of the vertice - ffw::vec3f v = cubeVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); + ffw::vec3f v = sm2obj::cubeVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); // Rotate it if(Rot == 0){ @@ -102,8 +105,8 @@ void addObjectCube(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, co // Add indices for(uint32_t i = 0, p = Output->indicesCount; i < 6; i++, p++){ // Add indices (starting from 0) to output plus the offset - Output->indices[p] = cubeFaces[i]-1 + Output->indicesOffset; - Output->indicesUvs[p] = cubeFacesUvs[i]; + Output->indices[p] = sm2obj::cubeFaces[i]-1 + Output->indicesOffset; + Output->indicesUvs[p] = sm2obj::cubeFacesUvs[i]; Output->indicesMat[p][0] = Block->id; Output->indicesMat[p][2] = Block->transparent; } @@ -121,11 +124,11 @@ void addObjectCube(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, co } ///============================================================================= -void addObjectXshape(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block){ +void addObjectXshape(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block){ // Add vertices for(uint32_t i = 0, p = Output->verticesCount; i < 8; i++, p++){ // Get vector of the vertice - ffw::vec3f v = XshapeVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); + ffw::vec3f v = sm2obj::XshapeVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); // Rotate it if(Rot == 0){ @@ -153,8 +156,8 @@ void addObjectXshape(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, // Add indices for(uint32_t i = 0, p = Output->indicesCount; i < 2; i++, p++){ // Add indices (starting from 0) to output plus the offset - Output->indices[p] = XshapeFaces[i]-1 + Output->indicesOffset; - Output->indicesUvs[p] = XshapeFacesUvs[i]; + Output->indices[p] = sm2obj::XshapeFaces[i]-1 + Output->indicesOffset; + Output->indicesUvs[p] = sm2obj::XshapeFacesUvs[i]; Output->indicesMat[p][0] = Block->id; Output->indicesMat[p][2] = uint16_t(Block->transparent) | (uint16_t(Block->object) << 8); Output->indicesMat[p][1] = Block->texture[0]; @@ -165,9 +168,9 @@ void addObjectXshape(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, } ///============================================================================= -void addObjectWedge(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block){ +void addObjectWedge(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block){ for(uint32_t i = 0, p = Output->verticesCount; i < 6; i++, p++){ - ffw::vec3f v = wedgeVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); + ffw::vec3f v = sm2obj::wedgeVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); if(Rot == 1){ v.rotateY(90); @@ -207,10 +210,10 @@ void addObjectWedge(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, c Output->verticesCount += 6; for(uint32_t i = 0, p = Output->indicesCount; i < 5; i++, p++){ - Output->indices[p] = wedgeFaces[i]-1 + Output->indicesOffset; - Output->indicesUvs[p] = wedgeFacesUvs[i]; + Output->indices[p] = sm2obj::wedgeFaces[i]-1 + Output->indicesOffset; + Output->indicesUvs[p] = sm2obj::wedgeFacesUvs[i]; - if(wedgeFaces[i].w == -1)Output->indices[p].w = -1; + if(sm2obj::wedgeFaces[i].w == -1)Output->indices[p].w = -1; Output->indicesMat[p][0] = Block->id; Output->indicesMat[p][1] = Block->texture[0]; Output->indicesMat[p][2] = Block->transparent; @@ -222,9 +225,9 @@ void addObjectWedge(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, c } ///============================================================================= -void addObjectHepta(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block){ +void addObjectHepta(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block){ for(uint32_t i = 0, p = Output->verticesCount; i < 7; i++, p++){ - ffw::vec3f v = heptaVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); + ffw::vec3f v = sm2obj::heptaVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); if(Rot == 1){ v.rotateY(-90); @@ -264,10 +267,10 @@ void addObjectHepta(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, c Output->verticesCount += 7; for(uint32_t i = 0, p = Output->indicesCount; i < 7; i++, p++){ - Output->indices[p] = heptaFaces[i]-1 + Output->indicesOffset; - Output->indicesUvs[p] = heptaFacesUvs[i]; + Output->indices[p] = sm2obj::heptaFaces[i]-1 + Output->indicesOffset; + Output->indicesUvs[p] = sm2obj::heptaFacesUvs[i]; - if(heptaFaces[i].w == -1)Output->indices[p].w = -1; + if(sm2obj::heptaFaces[i].w == -1)Output->indices[p].w = -1; Output->indicesMat[p][0] = Block->id; Output->indicesMat[p][1] = Block->texture[0]; Output->indicesMat[p][2] = Block->transparent; @@ -278,9 +281,9 @@ void addObjectHepta(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, c } ///============================================================================= -void addObjectTetra(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const blockInfoStruct* Block){ +void addObjectTetra(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, const sm2obj::blockInfoStruct* Block){ for(uint32_t i = 0, p = Output->verticesCount; i < 4; i++, p++){ - ffw::vec3f v = tetraVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); + ffw::vec3f v = sm2obj::tetraVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); if(Rot == 1){ v.rotateY(-90); @@ -309,10 +312,10 @@ void addObjectTetra(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, c Output->verticesCount += 4; for(uint32_t i = 0, p = Output->indicesCount; i < 4; i++, p++){ - Output->indices[p] = tetraFaces[i]-1 + Output->indicesOffset; - Output->indicesUvs[p] = tetraFacesUvs[i]; + Output->indices[p] = sm2obj::tetraFaces[i]-1 + Output->indicesOffset; + Output->indicesUvs[p] = sm2obj::tetraFacesUvs[i]; - if(tetraFaces[i].w == -1)Output->indices[p].w = -1; + if(sm2obj::tetraFaces[i].w == -1)Output->indices[p].w = -1; //chunkFacesMat[p] = ID; Output->indicesMat[p][0] = Block->id; Output->indicesMat[p][1] = Block->texture[0]; @@ -324,9 +327,9 @@ void addObjectTetra(chunkBufferStruct* Output, const ffw::vec3f& Pos, int Rot, c } ///============================================================================= -void addObjectCorner(chunkBufferStruct* Output, const ffw::vec3f& Pos, int RotA, int RotB, const blockInfoStruct* Block){ +void addObjectCorner(sm2obj::chunkBufferStruct* Output, const ffw::vec3f& Pos, int RotA, int RotB, const sm2obj::blockInfoStruct* Block){ for(uint32_t i = 0, p = Output->verticesCount; i < 5; i++, p++){ - ffw::vec3f v = cornerVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); + ffw::vec3f v = sm2obj::cornerVertices[i] + ffw::vec3f(-0.5f, -0.5f, +0.5f); if(RotB == 0 && RotA == 3){ @@ -401,10 +404,10 @@ void addObjectCorner(chunkBufferStruct* Output, const ffw::vec3f& Pos, int RotA, Output->verticesCount += 5; for(uint32_t i = 0, p = Output->indicesCount; i < 5; i++, p++){ - Output->indices[p] = cornerFaces[i]-1 + Output->indicesOffset; - Output->indicesUvs[p] = cornerFacesUvs[i]; + Output->indices[p] = sm2obj::cornerFaces[i]-1 + Output->indicesOffset; + Output->indicesUvs[p] = sm2obj::cornerFacesUvs[i]; - if(cornerFaces[i].w == -1)Output->indices[p].w = -1; + if(sm2obj::cornerFaces[i].w == -1)Output->indices[p].w = -1; //chunkFacesMat[p] = ID; Output->indicesMat[p][0] = Block->id; Output->indicesMat[p][1] = Block->texture[0]; diff --git a/source/exporter/blockConstructor.h b/source/exporter/blockConstructor.h new file mode 100644 index 0000000..c9c86aa --- /dev/null +++ b/source/exporter/blockConstructor.h @@ -0,0 +1,19 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_BLOCK_CONSTRUCTOR +#define SM2OBJ_BLOCK_CONSTRUCTOR + +#include +#include "config.h" + +namespace sm2obj{ + bool buildBlock(ffw::vec3i Pos, ffw::vec3i PosRel, ffw::vec3i PosFile, uint32_t ChunkData[][16][16], chunkBufferStruct* Output); +} + +#endif + + diff --git a/source/blockExtractor.cpp b/source/exporter/blockExtractor.cpp similarity index 52% rename from source/blockExtractor.cpp rename to source/exporter/blockExtractor.cpp index 5436e33..a634c1b 100644 --- a/source/blockExtractor.cpp +++ b/source/exporter/blockExtractor.cpp @@ -1,38 +1,23 @@ /* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License */ #include "blockExtractor.h" #include "blockConfig.h" #include "materialExport.h" -static uint64_t chunkFaceTotal = 0; -static std::vector extractedMaterials; -static std::vector extractedTiles; +//static uint64_t chunkFaceTotal = 0; +//static std::vector extractedMaterials; +//static std::vector extractedTiles; ///============================================================================= -void resetIndiceOffset(){ - chunkFaceTotal = 0; -} - -///============================================================================= -void resetMaterials(){ - extractedMaterials.clear(); - extractedTiles.clear(); -} - -///============================================================================= -const std::vector& getExtractedTiles(){ - return extractedTiles; -} - -///============================================================================= -bool loadRawBlocks(const std::string& ExecutablePath, chunkBufferStruct* Input, int ChunkIndex, int FileIndex){ +bool sm2obj::loadRawBlocks(const std::string& TempFolder, chunkBufferStruct* Input, int FileIndex){ ffw::file input; - if(!input.open(ExecutablePath + "\\temp\\" + ffw::valToString(FileIndex) + "_" + ffw::valToString(ChunkIndex) + ".raw.data", true, false, false)){ - ffw::logger().error() << "Error loading raw chunk data! Chunk index: " << ChunkIndex << " file index: " << FileIndex; + if(!input.open(TempFolder + "\\chunk-temp-processed-" + ffw::valToString(FileIndex) + ".raw", true, false, false)){ + LOG_ERROR("Failed to load processed raw chunk data! Chunk index: " + ffw::valToString(FileIndex)); return false; } @@ -69,11 +54,11 @@ bool loadRawBlocks(const std::string& ExecutablePath, chunkBufferStruct* Input, } ///============================================================================= -bool saveRawBlocks(const std::string& ExecutablePath, chunkBufferStruct* Input, int ChunkIndex, int FileIndex){ +bool sm2obj::saveRawBlocks(const std::string& TempFolder, chunkBufferStruct* Input, int FileIndex){ ffw::file output; - if(!output.open(ExecutablePath + "\\temp\\" + ffw::valToString(FileIndex) + "_" + ffw::valToString(ChunkIndex) + ".raw.data", true, true, true)){ - ffw::logger().error() << "Error saving raw chunk data! Chunk index: " << ChunkIndex << " file index: " << FileIndex; + if(!output.open(TempFolder + "\\chunk-temp-processed-" + ffw::valToString(FileIndex) + ".raw", true, true, true)){ + LOG_ERROR("Failed to svae processed raw chunk data! Chunk index: " + ffw::valToString(FileIndex)); return false; } @@ -110,17 +95,18 @@ bool saveRawBlocks(const std::string& ExecutablePath, chunkBufferStruct* Input, } ///============================================================================= -bool extractBlocks(const std::string& ExecutablePath, chunkBufferStruct* Input, int ChunkIndex, int FileIndex){ +bool sm2obj::extractBlocks(const std::string& TempFolder, chunkBufferStruct* Input, int FileIndex, + bool ExportMaterials, bool SplitTextures, bool ExportUvMaps, uint64_t* IndicesOffset, uint64_t* TexPosOffset){ ffw::file vertices; - ffw::file faces; + ffw::file indices; - if(!vertices.open(ExecutablePath + "\\temp\\" + ffw::valToString(FileIndex) + "_" + ffw::valToString(ChunkIndex) + ".vertices.data", false, true, true)){ - ffw::logger().error() << "Error saving vertices chunk data! Chunk index: " << ChunkIndex << " file index: " << FileIndex; + if(!vertices.open(TempFolder + "\\chunk-temp-" + ffw::valToString(FileIndex) + ".vertices", false, true, true)){ + LOG_ERROR("Failed to svae chunk vertices! Chunk index: " + ffw::valToString(FileIndex)); return false; } - if(!faces.open(ExecutablePath + "\\temp\\" + ffw::valToString(FileIndex) + "_" + ffw::valToString(ChunkIndex) + ".faces.data", false, true, true)){ - ffw::logger().error() << "Error saving polygons chunk data! Chunk index: " << ChunkIndex << " file index: " << FileIndex; + if(!indices.open(TempFolder + "\\chunk-temp-" + ffw::valToString(FileIndex) + ".indices", false, true, true)){ + LOG_ERROR("Failed to svae chunk indices! Chunk index: " + ffw::valToString(FileIndex)); return false; } @@ -138,77 +124,80 @@ bool extractBlocks(const std::string& ExecutablePath, chunkBufferStruct* Input, int currentTextureOffset = 0; const blockInfoStruct* block = NULL; for(uint32_t i = 0; i < Input->indicesCount; i++){ - if(textureSplit && materialExport && (lastId != Input->indicesMat[i][0] || lastTex != Input->indicesMat[i][1])){ + if(SplitTextures && ExportMaterials && (lastId != Input->indicesMat[i][0] || lastTex != Input->indicesMat[i][1])){ lastId = Input->indicesMat[i][0]; lastTex = Input->indicesMat[i][1]; block = findBlock(lastId); if(block != NULL){ - faces.writeLine("usemtl Mat_" + block->name + "_" + ffw::valToString(lastTex)); + indices.writeLine("usemtl Mat_" + block->name + "_" + ffw::valToString(lastTex)); bool found = false; - for(const auto& item : extractedMaterials){ + for(const auto& item : Input->extractedMaterialsList){ if(item.x == lastId && item.y == lastTex){found = true; break;} } if(!found){ - addMaterial(block, lastTex); - extractedMaterials.push_back(ffw::vec2i(lastId, lastTex)); + //addMaterial(block, lastTex); + Input->extractedMaterialsList.push_back(ffw::vec2i(lastId, lastTex)); } } else { - faces.writeLine("usemtl Mat_Unknown_" + ffw::valToString(Input->indicesMat[i][0]) + "_" + ffw::valToString(lastTex)); + indices.writeLine("usemtl Mat_Unknown_" + ffw::valToString(Input->indicesMat[i][0]) + "_" + ffw::valToString(lastTex)); } } - if(!textureSplit && materialExport && lastTex != Input->indicesMat[i][1]){ + if(!SplitTextures && ExportMaterials && lastTex != Input->indicesMat[i][1]){ lastTex = Input->indicesMat[i][1]; if(lastAtlas != Input->indicesMat[i][1] / 256){ lastAtlas = Input->indicesMat[i][1] / 256; - faces.writeLine("usemtl Mat_Atlas_" + ffw::valToString(lastAtlas)); + indices.writeLine("usemtl Mat_Atlas_" + ffw::valToString(lastAtlas)); } bool found = false; - for(size_t f = 0; f < extractedTiles.size(); f++){ - if(extractedTiles[f] == Input->indicesMat[i][1]){found = true; currentTextureOffset = f; break;} + for(size_t f = 0; f < Input->extractedMaterialsList.size(); f++){ + if(Input->extractedMaterialsList[f].x == Input->indicesMat[i][1]){found = true; currentTextureOffset = f; break;} } if(!found){ - currentTextureOffset = extractedTiles.size(); - extractedTiles.push_back(Input->indicesMat[i][1]); + currentTextureOffset = Input->extractedMaterialsList.size(); + Input->extractedMaterialsList.push_back(ffw::vec2i(Input->indicesMat[i][1], -1)); } } - if(uvsExport){ + if(ExportUvMaps){ if(Input->indices[i].w == -1){ - faces.writeLine("f " + ffw::valToString(Input->indices[i].x + chunkFaceTotal +1) + "/" + ffw::valToString(Input->indicesUvs[i].x + currentTextureOffset*4) + " " - + ffw::valToString(Input->indices[i].y + chunkFaceTotal +1) + "/" + ffw::valToString(Input->indicesUvs[i].y + currentTextureOffset*4) + " " - + ffw::valToString(Input->indices[i].z + chunkFaceTotal +1) + "/" + ffw::valToString(Input->indicesUvs[i].z + currentTextureOffset*4)); + indices.writeLine("f " + ffw::valToString(Input->indices[i].x + (*IndicesOffset) +1) + "/" + ffw::valToString(Input->indicesUvs[i].x + (*TexPosOffset) + currentTextureOffset*4) + " " + + ffw::valToString(Input->indices[i].y + (*IndicesOffset) +1) + "/" + ffw::valToString(Input->indicesUvs[i].y + (*TexPosOffset) + currentTextureOffset*4) + " " + + ffw::valToString(Input->indices[i].z + (*IndicesOffset) +1) + "/" + ffw::valToString(Input->indicesUvs[i].z + (*TexPosOffset) + currentTextureOffset*4)); } else { - faces.writeLine("f " + ffw::valToString(Input->indices[i].x + chunkFaceTotal +1) + "/" + ffw::valToString(Input->indicesUvs[i].x + currentTextureOffset*4) + " " - + ffw::valToString(Input->indices[i].y + chunkFaceTotal +1) + "/" + ffw::valToString(Input->indicesUvs[i].y + currentTextureOffset*4) + " " - + ffw::valToString(Input->indices[i].z + chunkFaceTotal +1) + "/" + ffw::valToString(Input->indicesUvs[i].z + currentTextureOffset*4) + " " - + ffw::valToString(Input->indices[i].w + chunkFaceTotal +1) + "/" + ffw::valToString(Input->indicesUvs[i].w + currentTextureOffset*4)); + indices.writeLine("f " + ffw::valToString(Input->indices[i].x + (*IndicesOffset) +1) + "/" + ffw::valToString(Input->indicesUvs[i].x + (*TexPosOffset) + currentTextureOffset*4) + " " + + ffw::valToString(Input->indices[i].y + (*IndicesOffset) +1) + "/" + ffw::valToString(Input->indicesUvs[i].y + (*TexPosOffset) + currentTextureOffset*4) + " " + + ffw::valToString(Input->indices[i].z + (*IndicesOffset) +1) + "/" + ffw::valToString(Input->indicesUvs[i].z + (*TexPosOffset) + currentTextureOffset*4) + " " + + ffw::valToString(Input->indices[i].w + (*IndicesOffset) +1) + "/" + ffw::valToString(Input->indicesUvs[i].w + (*TexPosOffset) + currentTextureOffset*4)); } } else { if(Input->indices[i].w == -1){ - faces.writeLine("f " + ffw::valToString(Input->indices[i].x + chunkFaceTotal +1) + " " - + ffw::valToString(Input->indices[i].y + chunkFaceTotal +1) + " " - + ffw::valToString(Input->indices[i].z + chunkFaceTotal +1)); + indices.writeLine("f " + ffw::valToString(Input->indices[i].x + (*IndicesOffset) +1) + " " + + ffw::valToString(Input->indices[i].y + (*IndicesOffset) +1) + " " + + ffw::valToString(Input->indices[i].z + (*IndicesOffset) +1)); } else { - faces.writeLine("f " + ffw::valToString(Input->indices[i].x + chunkFaceTotal +1) + " " - + ffw::valToString(Input->indices[i].y + chunkFaceTotal +1) + " " - + ffw::valToString(Input->indices[i].z + chunkFaceTotal +1) + " " - + ffw::valToString(Input->indices[i].w + chunkFaceTotal +1)); + indices.writeLine("f " + ffw::valToString(Input->indices[i].x + (*IndicesOffset) +1) + " " + + ffw::valToString(Input->indices[i].y + (*IndicesOffset) +1) + " " + + ffw::valToString(Input->indices[i].z + (*IndicesOffset) +1) + " " + + ffw::valToString(Input->indices[i].w + (*IndicesOffset) +1)); } } } - chunkFaceTotal += Input->verticesCount; + *IndicesOffset += Input->verticesCount; + + //if(SplitTextures)*TexPosOffset += 4; + //else *TexPosOffset += currentTextureOffset; - faces.close(); + indices.close(); return true; } diff --git a/source/exporter/blockExtractor.h b/source/exporter/blockExtractor.h new file mode 100644 index 0000000..6469715 --- /dev/null +++ b/source/exporter/blockExtractor.h @@ -0,0 +1,25 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_BLOCK_EXTRACTOR +#define SM2OBJ_BLOCK_EXTRACTOR + +#include +#include "config.h" + +namespace sm2obj{ + //void resetIndiceOffset(); + //void resetMaterials(); + //const std::vector& getExtractedTiles(); + bool saveRawBlocks(const std::string& TempFolder, chunkBufferStruct* Input, int FileIndex); + bool loadRawBlocks(const std::string& TempFolder, chunkBufferStruct* Input, int FileIndex); + bool extractBlocks(const std::string& TempFolder, chunkBufferStruct* Input, int FileIndex, bool ExportMaterials, bool SplitTextures, bool ExportUvMaps, uint64_t* IndicesOffset, uint64_t* TexPosOffset); +} + +#endif + + + diff --git a/source/blockVerticeData.cpp b/source/exporter/blockVerticeData.cpp similarity index 82% rename from source/blockVerticeData.cpp rename to source/exporter/blockVerticeData.cpp index b599965..27d7710 100644 --- a/source/blockVerticeData.cpp +++ b/source/exporter/blockVerticeData.cpp @@ -1,6 +1,7 @@ /* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License */ #include "blockVerticeData.h" @@ -13,14 +14,14 @@ };*/ // Mirrored -ffw::vec2f globalTextureUvs[4] = { +ffw::vec2f sm2obj::globalTextureUvs[4] = { ffw::vec2f(0.000000, 0.000000), ffw::vec2f(0.000000, 1.000000), ffw::vec2f(1.000000, 1.000000), ffw::vec2f(1.000000, 0.000000) }; -ffw::vec3f cubeVertices[8] = { +ffw::vec3f sm2obj::cubeVertices[8] = { ffw::vec3f(1.000000, 1.000000, 0.000000), ffw::vec3f(1.000000, -0.000000, -0.000000), ffw::vec3f(1.000000, 0.000000, -1.000000), @@ -31,7 +32,7 @@ ffw::vec3f cubeVertices[8] = { ffw::vec3f(0.000000, 0.000000, -1.000000) }; -ffw::vec4i cubeFaces[6] = { +ffw::vec4i sm2obj::cubeFaces[6] = { ffw::vec4i(4, 3, 8, 7), // Top ffw::vec4i(2, 1, 5, 6), // Bottom ffw::vec4i(1, 4, 7, 5), // Front @@ -40,7 +41,7 @@ ffw::vec4i cubeFaces[6] = { ffw::vec4i(7, 8, 6, 5) // Right }; -ffw::vec4i cubeFacesUvs[6] = { +ffw::vec4i sm2obj::cubeFacesUvs[6] = { ffw::vec4i(1, 2, 3, 4), // 1 2 3 4 ffw::vec4i(3, 4, 1, 2), // 4 1 2 3 ffw::vec4i(3, 4, 1, 2), // 1 2 3 4 @@ -51,7 +52,7 @@ ffw::vec4i cubeFacesUvs[6] = { -ffw::vec3f wedgeVertices[6] = { +ffw::vec3f sm2obj::wedgeVertices[6] = { ffw::vec3f(0.000000, 0.000000, 0.000000), ffw::vec3f(1.000000, 0.000000, 0.000000), ffw::vec3f(1.000000, 1.000000, 0.000000), @@ -60,7 +61,7 @@ ffw::vec3f wedgeVertices[6] = { ffw::vec3f(1.000000, 0.000000, -1.000000) }; -ffw::vec4i wedgeFaces[5] = { +ffw::vec4i sm2obj::wedgeFaces[5] = { ffw::vec4i(1, 2, 3, 4), ffw::vec4i(1, 4, 5, -1), ffw::vec4i(2, 1, 5, 6), @@ -68,7 +69,7 @@ ffw::vec4i wedgeFaces[5] = { ffw::vec4i(2, 6, 3, -1) }; -ffw::vec4i wedgeFacesUvs[5] = { +ffw::vec4i sm2obj::wedgeFacesUvs[5] = { ffw::vec4i(4, 1, 2, 3), // 4 1 2 3 ffw::vec4i(1, 2, 4, -1), // 1 2 4 ffw::vec4i(2, 3, 4, 1), // 2 3 4 1 @@ -78,7 +79,7 @@ ffw::vec4i wedgeFacesUvs[5] = { -ffw::vec3f heptaVertices[7] = { +ffw::vec3f sm2obj::heptaVertices[7] = { ffw::vec3f(1.000000, 1.000000, 0.000000), ffw::vec3f(1.000000, 0.000000, 0.000000), ffw::vec3f(1.000000, 0.000000, -1.000000), @@ -88,7 +89,7 @@ ffw::vec3f heptaVertices[7] = { ffw::vec3f(0.000000, 0.000000, -1.000000) }; -ffw::vec4i heptaFaces[7] = { +ffw::vec4i sm2obj::heptaFaces[7] = { ffw::vec4i(1, 2, 3, -1), ffw::vec4i(2, 1, 4, 5), ffw::vec4i(6, 7, 5, 4), @@ -98,7 +99,7 @@ ffw::vec4i heptaFaces[7] = { ffw::vec4i(6, 1, 3, -1) }; -ffw::vec4i heptaFacesUvs[7] = { +ffw::vec4i sm2obj::heptaFacesUvs[7] = { ffw::vec4i(3, 4, 1, -1), // 3 4 1 ffw::vec4i(1, 2, 3, 4), // 1 2 3 4 ffw::vec4i(1, 4, 3, 2), // 1 4 3 2 @@ -110,21 +111,21 @@ ffw::vec4i heptaFacesUvs[7] = { -ffw::vec3f tetraVertices[4] = { +ffw::vec3f sm2obj::tetraVertices[4] = { ffw::vec3f(-0.000000, 0.000000, 0.000000), ffw::vec3f(1.000000, 0.000000, 0.000000), ffw::vec3f(-0.000000, 1.000000, 0.000000), ffw::vec3f(-0.000000, 0.000000, -1.000000) }; -ffw::vec4i tetraFaces[4] = { +ffw::vec4i sm2obj::tetraFaces[4] = { ffw::vec4i(1, 2, 3, -1), ffw::vec4i(4, 1, 3, -1), ffw::vec4i(1, 4, 2, -1), ffw::vec4i(3, 2, 4, -1) }; -ffw::vec4i tetraFacesUvs[4] = { +ffw::vec4i sm2obj::tetraFacesUvs[4] = { ffw::vec4i(4, 1, 3, -1), // 4 1 3 ffw::vec4i(4, 1, 2, -1), // 4 1 2 ffw::vec4i(2, 3, 1, -1), // 2 3 1 @@ -133,7 +134,7 @@ ffw::vec4i tetraFacesUvs[4] = { -ffw::vec3f cornerVertices[5] = { +ffw::vec3f sm2obj::cornerVertices[5] = { ffw::vec3f(1.000000, 0.000000, -1.000000), ffw::vec3f(0.000000, 0.000000, -1.000000), ffw::vec3f(1.000000, 1.000000, 0.000000), @@ -141,7 +142,7 @@ ffw::vec3f cornerVertices[5] = { ffw::vec3f(0.000000, 0.000000, 0.000000) }; -ffw::vec4i cornerFaces[5] = { +ffw::vec4i sm2obj::cornerFaces[5] = { ffw::vec4i(1, 2, 3, -1), ffw::vec4i(4, 1, 3, -1), ffw::vec4i(5, 4, 3, -1), @@ -149,7 +150,7 @@ ffw::vec4i cornerFaces[5] = { ffw::vec4i(2, 5, 3, -1) }; -ffw::vec4i cornerFacesUvs[5] = { +ffw::vec4i sm2obj::cornerFacesUvs[5] = { ffw::vec4i(2, 3, 1, -1), // 2 3 1 ffw::vec4i(4, 1, 3, -1), // 4 1 3 ffw::vec4i(4, 1, 2, -1), // 4 1 2 @@ -158,7 +159,7 @@ ffw::vec4i cornerFacesUvs[5] = { }; -ffw::vec3f XshapeVertices[8] = { +ffw::vec3f sm2obj::XshapeVertices[8] = { ffw::vec3f(0.000000, 0.000000, -1.000000), ffw::vec3f(1.000000, 0.000000, 0.000000), ffw::vec3f(0.000000, 1.000000, -1.000000), @@ -169,12 +170,12 @@ ffw::vec3f XshapeVertices[8] = { ffw::vec3f(1.000000, 1.000000, -1.000000) }; -ffw::vec4i XshapeFaces[2] = { +ffw::vec4i sm2obj::XshapeFaces[2] = { ffw::vec4i(1, 2, 4, 3), ffw::vec4i(5, 6, 8, 7) }; -ffw::vec4i XshapeFacesUvs[2] = { +ffw::vec4i sm2obj::XshapeFacesUvs[2] = { ffw::vec4i(2, 3, 4, 1), ffw::vec4i(2, 3, 4, 1) }; diff --git a/source/exporter/blockVerticeData.h b/source/exporter/blockVerticeData.h new file mode 100644 index 0000000..7de63d9 --- /dev/null +++ b/source/exporter/blockVerticeData.h @@ -0,0 +1,43 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_BLOCK_VERTICE_DATA +#define SM2OBJ_BLOCK_VERTICE_DATA + +#include + +namespace sm2obj{ + extern ffw::vec2f globalTextureUvs[4]; + + extern ffw::vec3f cubeVertices[8]; + extern ffw::vec4i cubeFaces[6]; + extern ffw::vec4i cubeFacesUvs[6]; + + extern ffw::vec3f wedgeVertices[6]; + extern ffw::vec4i wedgeFaces[5]; + extern ffw::vec4i wedgeFacesUvs[5]; + + extern ffw::vec3f heptaVertices[7]; + extern ffw::vec4i heptaFaces[7]; + extern ffw::vec4i heptaFacesUvs[7]; + + extern ffw::vec3f tetraVertices[4]; + extern ffw::vec4i tetraFaces[4]; + extern ffw::vec4i tetraFacesUvs[4]; + + extern ffw::vec3f cornerVertices[5]; + extern ffw::vec4i cornerFaces[5]; + extern ffw::vec4i cornerFacesUvs[5]; + + extern ffw::vec3f XshapeVertices[8]; + extern ffw::vec4i XshapeFaces[2]; + extern ffw::vec4i XshapeFacesUvs[2]; +}; + +#endif + + + diff --git a/source/chunkHeader.cpp b/source/exporter/chunkHeader.cpp similarity index 88% rename from source/chunkHeader.cpp rename to source/exporter/chunkHeader.cpp index 0f567c3..82e1cf6 100644 --- a/source/chunkHeader.cpp +++ b/source/exporter/chunkHeader.cpp @@ -1,6 +1,7 @@ /* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License */ #include "chunkHeader.h" @@ -11,7 +12,7 @@ struct chunkIndexS { }; ///============================================================================= -bool loadChunkHeader(ffw::file* File, int* ChunkIndex, int* TotalChunks){ +bool sm2obj::loadChunkHeader(ffw::file* File, int* ChunkIndex, int* TotalChunks){ uint32_t unknownInt; chunkIndexS chunkIndex[16][16][16]; diff --git a/source/exporter/chunkHeader.h b/source/exporter/chunkHeader.h new file mode 100644 index 0000000..8ec3ea8 --- /dev/null +++ b/source/exporter/chunkHeader.h @@ -0,0 +1,19 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_CHUNK_HEADER +#define SM2OBJ_CHUNK_HEADER + +#include + +namespace sm2obj{ + bool loadChunkHeader(ffw::file* File, int* ChunkIndex, int* TotalChunks); +} + +#endif + + + diff --git a/source/chunkLoader.cpp b/source/exporter/chunkLoader.cpp similarity index 82% rename from source/chunkLoader.cpp rename to source/exporter/chunkLoader.cpp index 7820865..1535dcf 100644 --- a/source/chunkLoader.cpp +++ b/source/exporter/chunkLoader.cpp @@ -1,15 +1,17 @@ /* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License */ #include "chunkLoader.h" +#include "config.h" #include // Load chunk and decompress data ///============================================================================= -bool loadChunk(ffw::file* File, size_t FileOffset, int ChunkIndex, uint32_t ChunkData[][16][16], ffw::vec3i* Pos){ +bool sm2obj::loadChunk(ffw::file* File, size_t FileOffset, int ChunkIndex, uint32_t ChunkData[][16][16], ffw::vec3i* Pos){ uint64_t timeStamp; int32_t relativePosition[3]; uint8_t chunkType; @@ -22,7 +24,7 @@ bool loadChunk(ffw::file* File, size_t FileOffset, int ChunkIndex, uint32_t Chun File->read(&chunkType, sizeof(uint8_t)) && File->read(&compressedDataLength, sizeof(uint32_t)) && File->read(&compressedData, 5095)) ){ - ffw::logger().warning() << "Chunk error while reading chunk header!"; + LOG_ERROR("Chunk error while reading chunk header!"); return false; } @@ -35,7 +37,7 @@ bool loadChunk(ffw::file* File, size_t FileOffset, int ChunkIndex, uint32_t Chun if(relativePosition[0] > 9999 || relativePosition[0] < -9999 || relativePosition[1] > 9999 || relativePosition[1] < -9999 || relativePosition[2] > 9999 || relativePosition[2] < -9999){ - ffw::logger().warning() << "Chunk error while reading chunk header! Wrong relative position!"; + LOG_WARNING("Chunk error while reading chunk header! Wrong relative position!"); return false; } @@ -47,7 +49,7 @@ bool loadChunk(ffw::file* File, size_t FileOffset, int ChunkIndex, uint32_t Chun strm.next_in = Z_NULL; int ret = inflateInit(&strm); if(ret != Z_OK){ - ffw::logger().warning() << "Chunk failed to load! Z-Lib init error!"; + LOG_WARNING("Chunk failed to load! Z-Lib init error!"); inflateEnd(&strm); return false; } @@ -63,7 +65,7 @@ bool loadChunk(ffw::file* File, size_t FileOffset, int ChunkIndex, uint32_t Chun int have = (16*16*16*3) - strm.avail_out; if(have != 12288){ - ffw::logger().warning() << "Can not decompress data! Chunk might be corrupted!"; + LOG_WARNING("Can not decompress data! Chunk might be corrupted!"); return false; } diff --git a/source/exporter/chunkLoader.h b/source/exporter/chunkLoader.h new file mode 100644 index 0000000..e6e5ac3 --- /dev/null +++ b/source/exporter/chunkLoader.h @@ -0,0 +1,20 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_CHUNK_LOADER +#define SM2OBJ_CHUNK_LOADER + +#include + +namespace sm2obj{ + bool loadChunk(ffw::file* File, size_t FileOffset, int ChunkIndex, uint32_t ChunkData[][16][16], ffw::vec3i* Pos); +} + +#endif + + + + diff --git a/source/exporter/chunkTempLoader.cpp b/source/exporter/chunkTempLoader.cpp new file mode 100644 index 0000000..1059162 --- /dev/null +++ b/source/exporter/chunkTempLoader.cpp @@ -0,0 +1,27 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include "chunkTempLoader.h" +#include "config.h" + +///============================================================================= +bool sm2obj::saveTempChunk(const std::string& Folder, int FileIndex, uint32_t* Buffer){ + ffw::file input; + if(!input.open(Folder + "\\chunk-temp-"+ ffw::valToString(FileIndex) + ".raw", true, true, true))return false; + if(!input.write(Buffer, SM2OBJ_CHUNK_SIZE*sizeof(uint32_t)))return false; + input.close(); + return true; +} + +///============================================================================= +bool sm2obj::loadTempChunk(const std::string& Folder, int FileIndex, uint32_t* Buffer){ + ffw::file input; + if(!input.open(Folder + "\\chunk-temp-"+ ffw::valToString(FileIndex) + ".raw", true, false, false))return false; + if(input.getSize() < SM2OBJ_CHUNK_SIZE*sizeof(uint32_t))return false; + if(!input.read(Buffer, SM2OBJ_CHUNK_SIZE*sizeof(uint32_t)))return false; + input.close(); + return true; +} diff --git a/source/exporter/chunkTempLoader.h b/source/exporter/chunkTempLoader.h new file mode 100644 index 0000000..7c18437 --- /dev/null +++ b/source/exporter/chunkTempLoader.h @@ -0,0 +1,18 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_CHUNK_TEMP_LOADER +#define SM2OBJ_CHUNK_TEMP_LOADER + +#include + +namespace sm2obj{ + bool saveTempChunk(const std::string& Path, int FileIndex, uint32_t* Buffer); + bool loadTempChunk(const std::string& Path, int FileIndex, uint32_t* Buffer); +} + +#endif + diff --git a/source/exporter/config.cpp b/source/exporter/config.cpp new file mode 100644 index 0000000..2e9384d --- /dev/null +++ b/source/exporter/config.cpp @@ -0,0 +1,20 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include "config.h" + +sm2obj::chunkBufferStruct sm2obj::chunkBufferA; +sm2obj::chunkBufferStruct sm2obj::chunkBufferB; + +std::string sm2obj::config::imageExtension; +sm2obj::config::imageSaver sm2obj::config::imageSaverFunc; +sm2obj::config::exportLogError sm2obj::config::exportLogErrorFunc; +sm2obj::config::exportLogInfo sm2obj::config::exportLogInfoFunc; +sm2obj::config::exportLogDebug sm2obj::config::exportLogDebugFunc; +sm2obj::config::exportProgress sm2obj::config::exportProgressFunc; +sm2obj::config::exportLogWarning sm2obj::config::exportLogWarningFunc; +sm2obj::config::exportExit sm2obj::config::exportExitFunc; + diff --git a/source/exporter/config.h b/source/exporter/config.h new file mode 100644 index 0000000..5d82b95 --- /dev/null +++ b/source/exporter/config.h @@ -0,0 +1,45 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_CONFIG +#define SM2OBJ_CONFIG + +#include +#include "constants.h" +#include "structures.h" + +namespace sm2obj{ + extern chunkBufferStruct chunkBufferA; + extern chunkBufferStruct chunkBufferB; + + namespace config { + // Pointer to image saving function + // Use one of these: + // imageSaver = &ffw::saveBMP; + // imageSaver = &ffw::savePNG; + // imageSaver = &ffw::saveTGA; + // imageSaver = &ffw::saveTIFF; + extern std::string imageExtension; + + typedef bool (*imageSaver)(const std::string& Path, unsigned char* Pixels, int Width, int Height, ffw::imageType Type); + typedef void (*exportLogError)(const std::string& Message); + typedef void (*exportLogInfo)(const std::string& Message); + typedef void (*exportLogDebug)(const std::string& Message); + typedef void (*exportLogWarning)(const std::string& Message); + typedef void (*exportProgress)(int Progress, int Total); + typedef void (*exportExit)(bool Success); + + extern imageSaver imageSaverFunc; + extern exportLogError exportLogErrorFunc; + extern exportLogInfo exportLogInfoFunc; + extern exportLogDebug exportLogDebugFunc; + extern exportProgress exportProgressFunc; + extern exportLogWarning exportLogWarningFunc; + extern exportExit exportExitFunc; + }; +}; + +#endif diff --git a/source/exporter/constants.h b/source/exporter/constants.h new file mode 100644 index 0000000..d00201d --- /dev/null +++ b/source/exporter/constants.h @@ -0,0 +1,27 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#define SM2OBJ_CHUNK_SIZE 16*16*16 +#define SM2OBJ_CHUNK_MAX_VERTICES 8 +#define SM2OBJ_CHUNK_MAX_INDICES 8 + +#define TERMINATE(X)\ +sm2obj::config::exportExitFunc(X); return X; + +#define LOG_DEBUG(X)\ +sm2obj::config::exportLogDebugFunc(X); + +#define LOG_ERROR(X)\ +sm2obj::config::exportLogErrorFunc(X); + +#define LOG_INFO(X)\ +sm2obj::config::exportLogInfoFunc(X); + +#define LOG_WARNING(X)\ +sm2obj::config::exportLogWarningFunc(X); + +#define PROGRESS(X, Y)\ +sm2obj::config::exportProgressFunc(X, Y); diff --git a/source/exporter/exportBlueprint.cpp b/source/exporter/exportBlueprint.cpp new file mode 100644 index 0000000..0c8a169 --- /dev/null +++ b/source/exporter/exportBlueprint.cpp @@ -0,0 +1,1147 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include +#include + +#include "exportBlueprint.h" +#include "blockConfig.h" +#include "loadMeta.h" +#include "chunkTempLoader.h" +#include "chunkLoader.h" +#include "chunkHeader.h" +#include "blockConstructor.h" +#include "polygonFiltering.h" +#include "blockExtractor.h" +#include "blockVerticeData.h" +#include "materialExport.h" + +static bool recursiveMetaLoader(sm2obj::entityInfoStruct* Output, const std::string& InputFolder, const std::string& SubFolder, const std::string& OutputName); +static void recursiveMetaDump(sm2obj::entityInfoStruct* Output, int Indent); +static bool recursiveChunkLoader(sm2obj::entityInfoStruct* Input, const std::string& InputFolder, const std::string& OutputTemp, int& FileIndex); +static void recursiveChunkExport(sm2obj::entityInfoStruct* Input, const std::string& InputOutputTemp, sm2obj::threadInfoStruct* Threads, int NumOfThreads, int& Progress, int ProgressTotal); +static bool mergeVertices(sm2obj::entityInfoStruct* Input, const std::string& InputOutputTemp, ffw::file* Output, bool SplitTextures, bool ExportUvMaps); +static void generateEntityQueue(sm2obj::entityInfoStruct* Input, std::vector* Output); +static void* processChunk(void* ThreadInfoPtr); + +///============================================================================= +bool sm2obj::exportBlueprint(const std::string& InputConfigFolder, const std::string& InputFolder, + const std::string& OutputFolder, const std::string& OutputName, + bool ExportUV, bool UseAtlas, bool ExportMaterials, + bool ExportDiffuse, bool ExportAlpha, bool ExportNormals, bool ExportEmissive, + bool SpecularHighlight, int NumOfThreads, bool ExportAttachments){ + clearBlockConfig(); + ffw::file objOutput; + ffw::file mtlOutput; + entityInfoStruct entityRoot; + entityRoot.name = OutputName; + entityRoot.path = ""; + entityRoot.pos.set(8.0f, 8.0f, 8.0f); + entityRoot.dockModule.set(8.0f, 8.0f, 8.0f); + entityRoot.parent = NULL; + //entityRoot.model.translate(-8.0f, -8.0f, -8.0f); + threadInfoStruct* threads; + + // Load block types + if(!loadBlockTypes(InputConfigFolder + "\\BlockTypes.properties")){ + TERMINATE(false); + } + + // Load block config + if(!loadBlockConfig(InputConfigFolder + "\\BlockConfig.xml")){ + TERMINATE(false); + } + + // Open OBJ file for wirting + if(!objOutput.open(OutputFolder + "\\" + OutputName + ".obj", false, true, true)){ + LOG_ERROR("Failed to open OBJ file for writing! Make sure the path: " + OutputFolder + " is valid!"); + TERMINATE(false); + } + + // Open MTL file for writing + if(!mtlOutput.open(OutputFolder + "\\" + OutputName + ".mtl", false, true, true)){ + LOG_ERROR("Failed to open MTL file for writing! Make sure the path: " + OutputFolder + " is valid!"); + TERMINATE(false); + } + + // Load all attachments + if(ExportAttachments){ + LOG_INFO("Loading attached ships and turrets..."); + if(!recursiveMetaLoader(&entityRoot, InputFolder, "", OutputName)){ + TERMINATE(false); + } + } + + // Dump information about attachments + LOG_DEBUG("Blueprint root:"); + recursiveMetaDump(&entityRoot, 0); + + std::string tempFolder = ffw::getExecutablePath() + "\\temp"; + + // Load chunk headers and export decompressed chunks to temp folder + LOG_INFO("Loading chunks..."); + int fileIndex = 0; + ffw::createDirectory(tempFolder); + if(!recursiveChunkLoader(&entityRoot, InputFolder, ffw::getExecutablePath() + "\\temp", fileIndex)){ + TERMINATE(false); + } + + // Create threads + threads = new threadInfoStruct[NumOfThreads]; + for(int i = 0; i < NumOfThreads; i++){ + threads[i].thread.bindFunction(&processChunk); + } + + // Export chunks + int progress = 0; + int progressTotal = fileIndex; + recursiveChunkExport(&entityRoot, tempFolder, threads, NumOfThreads, progress, progressTotal); + + // Delete threads + delete[] threads; + + // Generate queue + std::vector entityQueue; + generateEntityQueue(&entityRoot, &entityQueue); + + // Export vertices + progress = 0; + uint64_t indicesOffset = 0; + uint64_t texPosOffset = 0; + sm2obj::chunkBufferStruct chunkRawBuffer; + //recursiveVerticesExport(&entityRoot, tempFolder, progress, progressTotal, ExportMaterials, !UseAtlas, ExportUV, &totalExportedIndices); + for(auto& entity : entityQueue){ + chunkRawBuffer.extractedMaterialsList.clear(); + for(auto& chunk : entity->chunks){ + progress++; + LOG_INFO("Generating vertices: " + ffw::valToString(progress) + " out of: " + ffw::valToString(progressTotal)); + PROGRESS(progress, progressTotal); + // Load a temp block to memory + sm2obj::loadRawBlocks(tempFolder, &chunkRawBuffer, chunk.fileIndex); + // Export block block to temp as indices and vertices + sm2obj::extractBlocks(tempFolder, &chunkRawBuffer, chunk.fileIndex, ExportMaterials, !UseAtlas, ExportUV, &indicesOffset, &texPosOffset); + } + + if(!UseAtlas)texPosOffset += 4; + else texPosOffset += chunkRawBuffer.extractedMaterialsList.size()*4; + + if(UseAtlas){ + for(auto i : chunkRawBuffer.extractedMaterialsList)entity->extractedTiles.push_back(i.x); + } else { + for(auto i : chunkRawBuffer.extractedMaterialsList)entity->extractedMaterials.push_back(i); + } + } + + // Merge files + LOG_INFO("Merging vertices and indices into a single file..."); + + objOutput.writeLine("# SM2OBJ"); + if(ExportMaterials)objOutput.writeLine("mtllib " + OutputName + ".mtl"); + + //recursiveVerticesMerge(&entityRoot, tempFolder, &objOutput, !UseAtlas, ExportUV); + for(auto& entity : entityQueue){ + mergeVertices(entity, tempFolder, &objOutput, !UseAtlas, ExportUV); + } + + LOG_INFO("Creating material file..."); + + // Export materials + if(ExportMaterials && UseAtlas){ + LOG_DEBUG("Exporting altas materials"); + LOG_DEBUG("Creating 3 materials..."); + createMaterialAtlas(&mtlOutput, ExportDiffuse, ExportNormals, ExportAlpha, ExportEmissive, SpecularHighlight); + } else if(ExportMaterials){ + LOG_DEBUG("Exporting block materials"); + std::vector materialList; + + for(auto& entity : entityQueue){ + for(auto& mat :entity->extractedMaterials){ + // Find if material is already added + bool found = false; + for(auto& item : materialList){ + if(item == mat){ + found = true; + break; + } + } + if(found)continue; + // Add to list + materialList.push_back(mat); + } + } + + LOG_DEBUG("Creating " + ffw::valToString(materialList.size()) + " materials..."); + + mtlOutput.writeLine("# SM2OBJ\n"); + for(auto& mat : materialList){ + const blockInfoStruct* block = findBlock(mat.x); + if(block == NULL)continue; + createMaterialTile(&mtlOutput, block, mat.y, ExportDiffuse, ExportNormals, ExportAlpha, ExportEmissive, SpecularHighlight); + } + } + + TERMINATE(true); +} + +///============================================================================= +bool recursiveMetaLoader(sm2obj::entityInfoStruct* Output, const std::string& InputFolder, const std::string& SubFolder, const std::string& OutputName){ + if(!loadMeta(Output, InputFolder + SubFolder + "\\meta.smbpm", OutputName)){ + return false; + } + + for(auto& item : Output->attachments){ + if(!recursiveMetaLoader(&item, InputFolder, "\\" + item.path, OutputName)){ + return false; + } + } + + return true; +} + +///============================================================================= +void recursiveMetaDump(sm2obj::entityInfoStruct* Output, int Indent){ + std::string ind; + ind.resize(Indent, ' '); + + LOG_DEBUG(ind + "> Name: " + Output->name); + LOG_DEBUG(ind + "> Path: " + Output->path); + //LOG_DEBUG(ind + "> Position: " + ffw::valToString(Output->pos.x) + ", " + ffw::valToString(Output->pos.y) + ", " + ffw::valToString(Output->pos.z)); + //LOG_DEBUG(ind + "> Orientation: " + ffw::valToString(Output->orientation.x) + ", " + ffw::valToString(Output->orientation.y) + ", " + ffw::valToString(Output->orientation.z)); + LOG_DEBUG(ind + "> Dock module position: " + ffw::valToString(Output->dockModule.x) + ", " + ffw::valToString(Output->dockModule.y) + ", " + ffw::valToString(Output->dockModule.z)); + for(auto& item : Output->attachments){ + recursiveMetaDump(&item, Indent+2); + } +} + +///============================================================================= +bool recursiveChunkLoader(sm2obj::entityInfoStruct* Input, const std::string& InputFolder, const std::string& OutputTemp, int& FileIndex){ + std::vector allFiles; + + // Open target directory + ffw::directory dir; + if(!dir.open(InputFolder + Input->path + "\\DATA")){ + LOG_ERROR("Failed to open folder: " + InputFolder + "\\" + Input->path + " The program might not have permission to do it!"); + return false; + } + + // Include all files that match + for(const auto& file : dir.getFiles()){ + std::string extension; + ffw::getFilePathProperties(file, NULL, NULL, &extension); + if(extension != "smd2")continue; + + allFiles.push_back(file); + } + + // Load all files + for(const auto& file : allFiles){ + ffw::file fileInput; + + LOG_DEBUG("Reading file: " + file); + + // Get file position from file name + size_t pos = file.find('.'); + if(pos == std::string::npos){ + LOG_ERROR("Error while retrieving file position from file name! Expected dot with coordinates!"); + return false; + } + + // Get position as tokens + std::vector tokens = ffw::getTokens(file.substr(pos+1, file.size()-pos-6), '.'); + if(tokens.size() != 3){ + LOG_ERROR("Error while retrieving file position from file name! Wrong coordinates!"); + return false; + } + + // Convert position to integers + ffw::vec3i filePos; + try { + filePos.x = ffw::stringToVal(tokens[0]); + filePos.y = ffw::stringToVal(tokens[1]); + filePos.z = ffw::stringToVal(tokens[2]); + filePos *= 256; + } catch (std::exception& e){ + LOG_ERROR("Error while retrieving file position from file name! Wrong coordinates!"); + return false; + } + + // Print file position + LOG_DEBUG("File position: " + tokens[0] + ", " + tokens[1] + ", " + tokens[2]); + + // Open a file + if(!fileInput.open(InputFolder + Input->path + "\\DATA\\" + file, true, false, false)){ + LOG_ERROR("Failed to open file: " + InputFolder + Input->path + "\\DATA\\" + file); + return false;\ + } + + // Check if file is big enough to contain header + size_t fileSize = fileInput.getSize(); + if(fileSize < size_t(4+32768+16384)){ + LOG_ERROR("File is invalid! File is too small. Expected at least 49156 bytes."); + return false; + } + + // Load header + int chunkIndex[SM2OBJ_CHUNK_SIZE]; + int totalChunks = 0; + if(!sm2obj::loadChunkHeader(&fileInput, &chunkIndex[0], &totalChunks)){ + //ffw::logger().error() << "Failed to load chunk header!"; + return false; + } + + // Remember the file offset + size_t fileOffset = fileInput.getPos(); + + LOG_DEBUG("Total chunks: " + ffw::valToString(totalChunks)); + //fileChunksTotal += totalChunks; + + // Sort indexes + // We need to load chunks ordered + std::sort(std::begin(chunkIndex), std::end(chunkIndex)); + + // Load all chunks + uint32_t chunkData[16][16][16]; + ffw::vec3i chunkPos; + for(int i = 0; i < SM2OBJ_CHUNK_SIZE; i++){ + if(chunkIndex[i] < 0)continue; + LOG_DEBUG("Loading chunk index: " + ffw::valToString(chunkIndex[i])); + + // Load chunk + fileInput.gotoPos(fileOffset + chunkIndex[i]*5120); + if(!sm2obj::loadChunk(&fileInput, fileOffset, chunkIndex[i], chunkData, &chunkPos)){ + continue; + } + + // Save decompressed raw chunk data to temp folder + if(!sm2obj::saveTempChunk(OutputTemp, FileIndex, &chunkData[0][0][0])){ + LOG_WARNING("Error while saving chunk to temp! Make sure the program has permissions to create files!"); + continue; + } + + // Add chunk to list + Input->chunks.push_back({chunkPos, filePos, FileIndex}); + FileIndex++; + } + } + + for(auto& item : Input->attachments){ + if(!recursiveChunkLoader(&item, InputFolder, OutputTemp, FileIndex))return false; + } + + return true; +} + +///============================================================================= +void recursiveChunkExport(sm2obj::entityInfoStruct* Input, const std::string& InputOutputTemp, sm2obj::threadInfoStruct* Threads, int NumOfThreads, int& Progress, int ProgressTotal){ + LOG_DEBUG("Exporting: " + Input->name + " from: " + Input->path); + + // export chunks one by one + for(auto& chunk : Input->chunks){ + // Find an empty thread + while(true){ + bool found = false; + // Loop through all threads + for(int i = 0; i < NumOfThreads; i++){ + // We can lock a thread only of it has finished + if(Threads[i].mut.tryLock()){ + // We found an empty thread, bind information and run it + Progress++; + //progress(processedChunks, fileChunksTotal); + LOG_INFO("Processing chunk: " + ffw::valToString(Progress) + " out of: " + ffw::valToString(ProgressTotal)); + PROGRESS(Progress, ProgressTotal); + // Where are raw chunks stored + Threads[i].tempFolder = InputOutputTemp; + // Unlock mutex + Threads[i].mut.unlock(); + // Always join even if thread has stopped + Threads[i].thread.join(); + // Bind chunk + Threads[i].chunkPtr = &chunk; + // Set matrix + Threads[i].entityPtr = Input; + // Start + Threads[i].thread.start(&Threads[i]); + // Wait 100ms and go to next chunk + ffw::usleep(100000); + found = true; + break; + } + } + // An empty thread was not found, wait and then try again + if(found)break; + ffw::usleep(100000); + } + } + + LOG_DEBUG("Waiting for threads..."); + // Join all threads + for(int i = 0; i < NumOfThreads; i++){ + Threads[i].thread.join(); + } + + for(auto& item : Input->attachments){ + recursiveChunkExport(&item, InputOutputTemp, Threads, NumOfThreads, Progress, ProgressTotal); + } +} + +void constructRotation(sm2obj::entityInfoStruct* Parent, ffw::mat4* Matrix){ + while(Parent != NULL){ + Matrix->rotate(Parent->orientation); + Parent = Parent->parent; + } +} + +///============================================================================= +void* processChunk(void* ThreadInfoPtr){ + // Get pointer to the thread info struct + sm2obj::threadInfoStruct* threadInfo = static_cast(ThreadInfoPtr); + // Lock this thread + threadInfo->mut.lock(); + // Get chunk pointer + sm2obj::chunkInfoStruct* chunk = threadInfo->chunkPtr; + + // Load chunk from file + uint32_t chunkData[16][16][16]; + if(!sm2obj::loadTempChunk(threadInfo->tempFolder, chunk->fileIndex, &chunkData[0][0][0])){ + LOG_WARNING("Error loading chunk file index: " + ffw::valToString(chunk->fileIndex)); + threadInfo->mut.unlock(); + return NULL; + } + + // Reset buffer A + threadInfo->chunkBufferA.indicesCount = 0; + threadInfo->chunkBufferA.verticesCount = 0; + threadInfo->chunkBufferA.indicesOffset = 0; + + // Build blocks by looking through all XYZ positions in chunk + ffw::vec3i posRel; + for(posRel.z = 0; posRel.z < 16; posRel.z++){ + for(posRel.y = 0; posRel.y < 16; posRel.y++){ + for(posRel.x = 0; posRel.x < 16; posRel.x++){ + if(chunkData[posRel.z][posRel.y][posRel.x] == 0)continue; + + buildBlock(posRel, chunk->pos, chunk->posFile, chunkData, &threadInfo->chunkBufferA); + } + } + } + + // Reset buffer B + threadInfo->chunkBufferB.indicesCount = 0; + threadInfo->chunkBufferB.verticesCount = 0; + threadInfo->chunkBufferB.indicesOffset = 0; + + // Filter polygons that occupies same space + removeDuplicatedFaces(&threadInfo->chunkBufferA, &threadInfo->chunkBufferB); + + // Now the data is in buffer B + // We will use buffer A as a output + threadInfo->chunkBufferA.indicesCount = 0; + threadInfo->chunkBufferA.verticesCount = 0; + threadInfo->chunkBufferA.indicesOffset = 0; + + // Remove duplicated vertices + removeDuplicatedVertices(&threadInfo->chunkBufferB, &threadInfo->chunkBufferA); + + // Transform vertices + ffw::vec3f translation(-8.0f, -8.0f, -8.0f); + ffw::vec3f dockOffset = threadInfo->entityPtr->dockModule - ffw::vec3f(8.0f, 8.0f, 8.0f); + translation -= dockOffset; + + /*ffw::mat4 m; + ffw::vec3f globalPos; + + sm2obj::entityInfoStruct* entity = threadInfo->entityPtr; + std::vector entityStack; + while(entity != NULL){ + entityStack.push_back(entity); + entity = entity->parent; + } + + if(entityStack.size() > 0){ + for(int i = entityStack.size()-1; i >= 0; i--){ + m.rotate(entityStack[i]->orientation); + } + } + + ffw::mat4 mr; + entity = threadInfo->entityPtr; + while(entity != NULL){ + + ffw::vec3f pos = (entity->pos - ffw::vec3f(8.0f, 8.0f, 8.0f)); + ffw::vec3f dockModule; + ffw::vec3f offset = entity->dockOffset; + //std::cout << "pos: " << pos << std::endl; + //std::cout << "dockOffset: " << offset << std::endl; + + //ffw::vec4f v; + ffw::mat4 rr; + constructRotation(entity, &rr); + + entity = entity->parent; + + if(entity != NULL){ + dockModule = (entity->dockModule - ffw::vec3f(8.0f, 8.0f, 8.0f)); + ffw::mat4 mr; + + constructRotation(entity, &mr); + + ffw::vec4f v; + v.x = pos.x; + v.y = pos.y; + v.z = pos.z; + v = mr * v; + pos.x = v.x; + pos.y = v.y; + pos.z = v.z; + + v.x = offset.x; + v.y = offset.y; + v.z = offset.z; + v.w = 1.0f; + v = mr * v; + offset.x = v.x; + offset.y = v.y; + offset.z = v.z; + + v.x = dockModule.x; + v.y = dockModule.y; + v.z = dockModule.z; + v.w = 1.0f; + v = mr * v; + dockModule.x = v.x; + dockModule.y = v.y; + dockModule.z = v.z; + } + globalPos -= dockModule; + globalPos += pos + offset; + }*/ + + for(uint32_t i = 0; i < threadInfo->chunkBufferA.verticesCount; i++){ + threadInfo->chunkBufferA.vertices[i] += translation; + /*ffw::vec4f v; + + v.x = threadInfo->chunkBufferA.vertices[i].x; + v.y = threadInfo->chunkBufferA.vertices[i].y; + v.z = threadInfo->chunkBufferA.vertices[i].z; + v.w = 1.0f; + v = m * v; + threadInfo->chunkBufferA.vertices[i].x = v.x; + threadInfo->chunkBufferA.vertices[i].y = v.y; + threadInfo->chunkBufferA.vertices[i].z = v.z; + threadInfo->chunkBufferA.vertices[i] += globalPos;*/ + } + + // Now the data is in buffer A + // Save it to temp file + saveRawBlocks(threadInfo->tempFolder, &threadInfo->chunkBufferA, chunk->fileIndex); + + LOG_DEBUG("Chunk index: " + ffw::valToString(chunk->fileIndex) + " exported!"); + + // Unlock this thread + threadInfo->mut.unlock(); + return NULL; +} + +///============================================================================= +void generateEntityQueue(sm2obj::entityInfoStruct* Input, std::vector* Output){ + Output->push_back(Input); + + for(auto& item : Input->attachments){ + generateEntityQueue(&item, Output); + } +} + +///============================================================================= +bool mergeVertices(sm2obj::entityInfoStruct* Input, const std::string& InputOutputTemp, ffw::file* Output, bool SplitTextures, bool ExportUvMaps){ + Output->writeLine("o " + Input->name); + + for(auto& chunk : Input->chunks){ + ffw::file vertices; + + // Open chunk temp file which contains vertices + if(!vertices.open(InputOutputTemp + "\\chunk-temp-" + ffw::valToString(chunk.fileIndex) + ".vertices", false, false, false)){ + LOG_ERROR("Error failed to open vertex temp data! Index: " + ffw::valToString(chunk.fileIndex)); + return false; + } + + // Read whole file and put contents in output OBJ + std::string temp; + while(!vertices.eof()){ + vertices.readLine(&temp); + if(temp.size() == 0)continue; + Output->writeLine(temp); + } + } + + // Export UVs + if(ExportUvMaps && SplitTextures){ + for(int i = 0; i < 4; i++){ + Output->writeLine("vt " + ffw::valToString(sm2obj::globalTextureUvs[i].x, 6) + " " + ffw::valToString(sm2obj::globalTextureUvs[i].y, 6)); + } + } else if(ExportUvMaps){ + for(const auto& tile : Input->extractedTiles){ + int atlasId = tile / 256; + int posY = tile / 16; + int posX = tile - posY*16; + posY -= atlasId*16; + ffw::vec2f tilePos(posX / 16.0f, posY / 16.0f); + tilePos.y = 1.0f - tilePos.y - 0.0625f; + + for(int i = 0; i < 4; i++){ + ffw::vec2f uvs = sm2obj::globalTextureUvs[i]; + uvs.y = 1.0f - uvs.y; + ffw::vec2f texPos = uvs * ffw::vec2f(0.052734375f, 0.052734375f) + tilePos + 0.0048828125f; + Output->writeLine("vt " + ffw::valToString(texPos.x, 10) + " " + ffw::valToString(texPos.y, 10)); + } + } + } + + // Shading off + Output->writeLine("s off"); + + // Do the same for indices + for(auto& chunk : Input->chunks){ + ffw::file indices; + + // Open chunk temp file which contains indices + if(!indices.open(InputOutputTemp + "\\chunk-temp-" + ffw::valToString(chunk.fileIndex) + ".indices", false, false, false)){ + LOG_ERROR("Error failed to open indice temp data! Index: " + ffw::valToString(chunk.fileIndex)); + return false; + } + + // Read whole file and put contents in output OBJ + std::string temp; + while(!indices.eof()){ + indices.readLine(&temp); + if(temp.size() == 0)continue; + Output->writeLine(temp); + } + } + + return true; +} + +/*#include "blockConfig.h" +#include "chunkHeader.h" +#include "chunkLoader.h" +#include "chunkTempLoader.h" +#include "config.h" +#include "blockConstructor.h" +#include "blockExtractor.h" +#include "polygonFiltering.h" +#include "textureExport.h" +#include "materialExport.h" +#include "blockVerticeData.h" + +#define TERMINATE(X)\ +if(exportExitFunc != NULL)exportExitFunc(X); return (void*)X; + +static ffw::vec3i boundingBoxMin; +static ffw::vec3i boundingBoxMax; +static int fileChunksTotal = 0; +static int processedChunks = 0; +static std::string executablePath; + +// Information about chunks +struct chunkInfoStruct { + // Relative position + ffw::vec3i pos; + // Relative position of the file from which chunk was loaded + ffw::vec3i posFile; + // Index of the chunk + int index; + // Index of the file + int indexFile; +}; + +// Information about thread +struct threadInfoStruct { + // Main thread class + ffw::thread thread; + // Lock mutex + ffw::mutex mut; + // Chunk information that is beeing processed by thread + chunkInfoStruct* chunkPtr; + // Buffers to save chunk data + chunkBufferStruct chunkBufferA; + chunkBufferStruct chunkBufferB; +}; + +static threadInfoStruct* threads; +static std::vector chunkInfo; + +///============================================================================= +void progress(int Value, int Range){ + if(exportProgressFunc != NULL)exportProgressFunc(Value, Range); +} + +///============================================================================= +void* processChunk(void* ThreadInfoPtr){ + // Get pointer to the thread info struct + threadInfoStruct* threadInfo = static_cast(ThreadInfoPtr); + // Lock this thread + threadInfo->mut.lock(); + // Get chunk pointer + chunkInfoStruct* chunk = threadInfo->chunkPtr; + + // Load chunk from file + uint32_t chunkData[16][16][16]; + if(!loadTempChunk(executablePath, chunk->index, chunk->indexFile, &chunkData[0][0][0])){ + ffw::logger().error() << "Error loading chunk index: " << chunk->index << " file: " << chunk->indexFile; + threadInfo->mut.unlock(); + return NULL; + } + + // Reset buffer A + threadInfo->chunkBufferA.indicesCount = 0; + threadInfo->chunkBufferA.verticesCount = 0; + threadInfo->chunkBufferA.indicesOffset = 0; + + // Build blocks by looking through all XYZ positions in chunk + ffw::vec3i posRel; + for(posRel.z = 0; posRel.z < 16; posRel.z++){ + for(posRel.y = 0; posRel.y < 16; posRel.y++){ + for(posRel.x = 0; posRel.x < 16; posRel.x++){ + if(chunkData[posRel.z][posRel.y][posRel.x] == 0)continue; + + buildBlock(posRel, chunk->pos, chunk->posFile, chunkData, &threadInfo->chunkBufferA); + } + } + } + + // Reset buffer B + threadInfo->chunkBufferB.indicesCount = 0; + threadInfo->chunkBufferB.verticesCount = 0; + threadInfo->chunkBufferB.indicesOffset = 0; + + // Filter polygons that occupies same space + removeDuplicatedFaces(&threadInfo->chunkBufferA, &threadInfo->chunkBufferB); + + // Now the data is in buffer B + // We will use buffer A as a output + threadInfo->chunkBufferA.indicesCount = 0; + threadInfo->chunkBufferA.verticesCount = 0; + threadInfo->chunkBufferA.indicesOffset = 0; + + // Remove duplicated vertices + removeDuplicatedVertices(&threadInfo->chunkBufferB, &threadInfo->chunkBufferA); + + // Now the data is in buffer A + // Save it to temp file + saveRawBlocks(executablePath, &threadInfo->chunkBufferA, chunk->index, chunk->indexFile); + + ffw::logger().debug() << "Chunk: " << chunk->index << " file: " << chunk->indexFile << " exported!"; + + // Unlock this thread + threadInfo->mut.unlock(); + return NULL; +} + +///============================================================================= +void* runExporter(void* DATA){ + chunkInfo.clear(); + fileChunksTotal = 0; + + // Initialize logger + ffw::logger::initLogger(); + + ffw::logger().print() << "Starting exporter..."; + + // Get executable path + executablePath = ffw::getExecutablePath(); + + // Check if there is a job to do + if(!textureExport && filePath.size() == 0){ + ffw::logger().error() << "Nothing to do..."; + TERMINATE(false); + } + + // Check if path to StarMade data folder is not empty + if(starMadeDataFolder.size() == 0){ + ffw::logger().error() << "Wrong StarMade data folder! Have you missed something?"; + TERMINATE(false); + } + + // Check if target file is not empty + if(filePath.size() == 0){ + ffw::logger().error() << "Wrong file name! Have you missed something?"; + TERMINATE(false); + } + + // Check if output file name is not empty + if(fileName.size() == 0){ + ffw::logger().error() << "Wrong file name! Have you missed something?"; + TERMINATE(false); + } + + // Check if file exists + if(!ffw::checkIfFileExists(filePath)){ + ffw::logger().error() << "Invalid path to blueprint! Maybe the program does not have permissions..."; + TERMINATE(false); + } + + // Separate path to folder, name, and to extension + std::string blueprintFolder; + std::string blueprintName; + std::string blueprintExtension; + ffw::getFilePathProperties(filePath, &blueprintFolder, &blueprintName, &blueprintExtension); + + // Get frist part of the blueprint file name + // this: "Isanth Type-Zero Mb.0.0.0" becames this: "Isanth Type-Zero Mb" + std::string blueprintNameShort; + size_t dot = blueprintName.find('.'); + if(dot == std::string::npos){ + ffw::logger().error() << "Selected file has invalid name..."; + TERMINATE(false); + } else { + blueprintNameShort = blueprintName.substr(0, dot); + } + + // Check for all files inside blueprint folder + // We need to include all files + std::vector allFiles; + allFiles.push_back(blueprintName + ".smd2"); + + // Open target directory + ffw::listDirectory dir; + if(!dir.open(blueprintFolder)){ + ffw::logger().error() << "Can not look into the blueprint folder! Maybe the program does not have permissions..."; + TERMINATE(false); + } + + // Open output OBJ file + ffw::file objOutput; + if(!objOutput.open(fileOutputFolder + "\\" + fileName + ".obj", false, true, false)){ + ffw::logger().error() << "Error failed to open OBJ for writing!"; + TERMINATE(false); + } + + // Include all files that match blueprint name + for(const auto& file : dir.getFiles()){ + if(file == allFiles[0])continue; + if(file.find(blueprintNameShort) == 0){ + ffw::logger().debug() << "Adding blueprint file: " << file; + allFiles.push_back(file); + } + } + + // If no output folder is defined then the default path is C:/ + if(fileOutputFolder.size() == 0)fileOutputFolder = "C:"; + + // Check file extension + if(blueprintExtension != "smd2"){ + ffw::logger().error() << "Wrong blueprint file! Expected a file with *.smd2 extension"; + TERMINATE(false); + } + + // Begin material export + if(materialExport){ + if(textureSplit && !beginMaterialExport()){ + TERMINATE(false); + } else if(!textureSplit && !createMaterialAtlas()){ + TERMINATE(false); + } + resetMaterials(); + ffw::logger().print() << "Exporting materials is enabled!"; + } + + // Print some debug information + ffw::logger().print() << "Starting with " << threadsCount << " threads!"; + ffw::logger().print() << "Target blueprint: " << blueprintFolder; + + // Create target directories + ffw::createDirectory(executablePath + "\\temp"); + ffw::createDirectory(fileOutputFolder); + + // Load block config + if(!loadBlockTypes(starMadeDataFolder + "\\config\\BlockTypes.properties")){ + ffw::logger().error() << "Failed to load block types from \"data\\BlockTypes.properties\" Check if file exists!"; + TERMINATE(false); + } else { + ffw::logger().print() << "Block types loaded!"; + } + + if(!loadBlockConfig(starMadeDataFolder + "\\config\\BlockConfig.xml")){ + ffw::logger().error() << "Failed to load block config from \"data\\BlockConfig.xml\" Check if file exists!"; + TERMINATE(false); + } else { + ffw::logger().print() << "Block config loaded!"; + } + + // Run texture export + if(textureExport && textureSplit){ + ffw::logger().print() << "Exporting textures..."; + ffw::createDirectory(fileOutputFolder + "\\textures"); + if(!exportTextures(starMadeDataFolder + "\\textures\\block\\Default\\256")){ + TERMINATE(false); + } + } else if(textureExport){ + ffw::logger().print() << "Exporting atlases..."; + ffw::createDirectory(fileOutputFolder + "\\atlases"); + if(!exportAtlases(starMadeDataFolder + "\\textures\\block\\Default\\256")){ + TERMINATE(false); + } + } + + ffw::logger().print() << "Exporting blueprint..."; + + // Load all files + int fileIndex = 0; + for(const auto& file : allFiles){ + ffw::file fileInput; + + ffw::logger().print() << "Reading file: " << file; + + // Get file position from file name + size_t pos = file.find('.'); + if(pos == std::string::npos){ + ffw::logger().error() << "Error while retrieving file position from file name! Expected dot with coordinates!"; + TERMINATE(false); + } + + // Get position as tokens + std::vector tokens = ffw::getTokens(file.substr(pos+1, file.size()-pos-6), '.'); + if(tokens.size() != 3){ + ffw::logger().error() << "Error while retrieving file position from file name! Wrong coordinates!"; + TERMINATE(false); + } + + // Convert position to integers + ffw::vec3i filePos; + try { + filePos.x = ffw::stringToVal(tokens[0]); + filePos.y = ffw::stringToVal(tokens[0]); + filePos.z = ffw::stringToVal(tokens[0]); + filePos *= 256; + } catch (std::exception& e){ + ffw::logger().error() << "Error while retrieving file position from file name! Wrong coordinates!"; + } + + // Print file position + ffw::logger().debug() << "File position: " << filePos; + + // Open a file + if(!fileInput.open(blueprintFolder + "\\" + file, true, false, false)){ + ffw::logger().error() << "Failed to open file: " << file; + TERMINATE(false); + } + + // Check if file is big enough to contain header + size_t fileSize = fileInput.getSize(); + if(fileSize < size_t(4+32768+16384)){ + ffw::logger().error() << "File is invalid! File is too small. Expected at least 49156 bytes."; + TERMINATE(false); + } + + // Load header + int chunkIndex[CHUNK_SIZE]; + int totalChunks = 0; + if(!loadChunkHeader(&fileInput, &chunkIndex[0], &totalChunks)){ + //ffw::logger().error() << "Failed to load chunk header!"; + TERMINATE(false); + } + + // Remember the file offset + size_t fileOffset = fileInput.getPos(); + + ffw::logger().debug() << "Total chunks: " << totalChunks; + fileChunksTotal += totalChunks; + + // Sort indexes + // We need to load chunks ordered + std::sort(std::begin(chunkIndex), std::end(chunkIndex)); + + // Load all chunks + uint32_t chunkData[16][16][16]; + ffw::vec3i chunkPos; + for(int i = 0; i < CHUNK_SIZE; i++){ + if(chunkIndex[i] < 0)continue; + ffw::logger().debug() << "Loading chunk index: " << chunkIndex[i]; + + // Load chunk + fileInput.gotoPos(fileOffset + chunkIndex[i]*5120); + if(!loadChunk(&fileInput, fileOffset, chunkIndex[i], chunkData, &chunkPos)){ + continue; + } + + // Calculate bounding box + if(chunkPos.x < boundingBoxMin.x)boundingBoxMin.x = chunkPos.x; + if(chunkPos.x+16 > boundingBoxMax.x)boundingBoxMax.x = chunkPos.x+16; + + if(chunkPos.y < boundingBoxMin.y)boundingBoxMin.y = chunkPos.y; + if(chunkPos.y+16 > boundingBoxMax.y)boundingBoxMax.y = chunkPos.y+16; + + if(chunkPos.z < boundingBoxMin.z)boundingBoxMin.z = chunkPos.z; + if(chunkPos.z+16 > boundingBoxMax.z)boundingBoxMax.z = chunkPos.z+16; + + // Save decompressed raw chunk data to temp folder + if(!saveTempChunk(executablePath, chunkIndex[i], fileIndex, &chunkData[0][0][0])){ + ffw::logger().warning() << "Error while saving chunk to temp!"; + continue; + } + + // All chunk to list + chunkInfo.push_back(chunkInfoStruct{chunkPos, filePos, chunkIndex[i], fileIndex}); + } + + fileIndex++; + } + + ffw::logger().print() << "Bounding box -X: " << boundingBoxMin.x << " +X: " << boundingBoxMax.x; + ffw::logger().print() << "Bounding box -Y: " << boundingBoxMin.y << " +Y: " << boundingBoxMax.y; + ffw::logger().print() << "Bounding box -Z: " << boundingBoxMin.z << " +Z: " << boundingBoxMax.z; + + // Create threads + threads = new threadInfoStruct[threadsCount]; + for(int i = 0; i < threadsCount; i++){ + threads[i].thread.bindFunction(&processChunk); + } + + // Start exporting... + ffw::logger().print() << "Starting threads..."; + + // export chunks one by one + processedChunks = 0; + for(auto& chunk : chunkInfo){ + // Find an empty thread + while(true){ + bool found = false; + // Loop through all threads + for(int i = 0; i < threadsCount; i++){ + // We can lock a thread only of it has finished + if(threads[i].mut.tryLock()){ + // We found an empty thread, bind information and run it + processedChunks++; + progress(processedChunks, fileChunksTotal); + ffw::logger().print() << "Processing: " << processedChunks << " out of: " << fileChunksTotal; + // Unlock mutex + threads[i].mut.unlock(); + // Always join even if thread has stopped + threads[i].thread.join(); + // Bind chunk + threads[i].chunkPtr = &chunk; + // Start + threads[i].thread.start(&threads[i]); + // Wait 100ms and go to next chunk + ffw::usleep(100000); + found = true; + break; + } + } + // An empty thread was not found, wait and then try again + if(found)break; + ffw::usleep(100000); + } + } + + ffw::logger().print() << "Waiting for threads..."; + // Join all threads + for(int i = 0; i < threadsCount; i++){ + threads[i].thread.join(); + } + + // Reset indice offset + resetIndiceOffset(); + + // Generate vertices for all chunks + processedChunks = 0; + chunkBufferStruct chunkRawBuffer; + for(auto& chunk : chunkInfo){ + processedChunks++; + ffw::logger().print() << "Generating vertices: " << processedChunks << " out of: " << fileChunksTotal; + // Load a temp block to memory + loadRawBlocks(executablePath, &chunkRawBuffer, chunk.index, chunk.indexFile); + // Export block block to temp as indices and vertices + extractBlocks(executablePath, &chunkRawBuffer, chunk.index, chunk.indexFile); + } + + // End material export, we do not need it anymore + if(textureSplit && materialExport)endMaterialExport(); + + ffw::logger().print() << "Merging vertices into single OBJ..."; + + // Merge files to one OBJ file + objOutput.writeLine("# StarMade Blueprint Exporter v1.0"); + if(materialExport)objOutput.writeLine("mtllib " + fileName + ".mtl"); + objOutput.writeLine("o " + fileName); + + // Export chunks one by one + for(const auto& chunk : chunkInfo){ + ffw::file vertices; + + // Open chunk temp file which contains vertices + if(!vertices.open(executablePath + "\\temp\\" + ffw::valToString(chunk.indexFile) + "_" + ffw::valToString(chunk.index) + ".vertices.data", false, false, false)){ + ffw::logger().error() << "Error failed to open faces temp data! Index: " << chunk.index << " file: " << chunk.indexFile; + TERMINATE(false); + } + + // Read whole file and put contents in output OBJ + std::string temp; + while(!vertices.eof()){ + vertices.readLine(&temp); + if(temp.size() == 0)continue; + objOutput.writeLine(temp); + } + } + + // Export UVs + if(uvsExport && textureSplit){ + for(int i = 0; i < 4; i++){ + objOutput.writeLine("vt " + ffw::valToString(globalTextureUvs[i].x, 6) + " " + ffw::valToString(globalTextureUvs[i].y, 6)); + } + } else if(uvsExport){ + for(const auto& tile : getExtractedTiles()){ + int atlasId = tile / 256; + int posY = tile / 16; + int posX = tile - posY*16; + posY -= atlasId*16; + ffw::vec2f tilePos(posX / 16.0f, posY / 16.0f); + tilePos.y = 1.0f - tilePos.y - 0.0625f; + + for(int i = 0; i < 4; i++){ + ffw::vec2f uvs = globalTextureUvs[i]; + uvs.y = 1.0f - uvs.y; + ffw::vec2f texPos = uvs * ffw::vec2f(0.052734375f, 0.052734375f) + tilePos + 0.0048828125f; + objOutput.writeLine("vt " + ffw::valToString(texPos.x, 10) + " " + ffw::valToString(texPos.y, 10)); + } + } + } + + // Shading off + objOutput.writeLine("s off"); + + // Do the same for indices + for(const auto& chunk : chunkInfo){ + ffw::file faces; + + // Open chunk temp file which contains indices + if(!faces.open(executablePath + "\\temp\\" + ffw::valToString(chunk.indexFile) + "_" + ffw::valToString(chunk.index) + ".faces.data", false, false, false)){ + ffw::logger().error() << "Error failed to open faces temp data! Index: " << chunk.index << " file: " << chunk.indexFile; + TERMINATE(false); + } + + // Read whole file and put contents in output OBJ + std::string temp; + while(!faces.eof()){ + faces.readLine(&temp); + if(temp.size() == 0)continue; + objOutput.writeLine(temp); + } + } + + // Delete threads + delete[] threads; + + // Remove temp folder + system(std::string("rmdir /Q /S " + executablePath + "\\temp").c_str()); + + // Print some debug information + ffw::logger().print() << "OBJ file saved to: " << fileOutputFolder << "\\" << fileName << ".obj"; + ffw::logger().print() << "Export finished with no critial errors!"; + + // Terminate with success + TERMINATE(true); +}*/ diff --git a/source/exporter/exportBlueprint.h b/source/exporter/exportBlueprint.h new file mode 100644 index 0000000..f7f871e --- /dev/null +++ b/source/exporter/exportBlueprint.h @@ -0,0 +1,20 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SMD2_EXPORT_BLUEPRINT +#define SMD2_EXPORT_BLUEPRINT + +#include "config.h" + +namespace sm2obj{ + bool exportBlueprint(const std::string& InputConfigFolder, const std::string& InputFolder, + const std::string& OutputFolder, const std::string& OutputName, + bool ExportUV, bool UseAtlas, bool ExportMaterials, + bool ExportDiffuse, bool ExportAlpha, bool ExportNormals, bool ExportEmissive, + bool SpecularHighlight, int NumOfThreads, bool ExportAttachments); +}; + +#endif diff --git a/source/exporter/loadMeta.cpp b/source/exporter/loadMeta.cpp new file mode 100644 index 0000000..872f3b4 --- /dev/null +++ b/source/exporter/loadMeta.cpp @@ -0,0 +1,258 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include "loadMeta.h" + +///============================================================================= +static size_t findName(ffw::file* Input, const std::string& Name){ + char c; + std::string test; + test.resize(Name.size()); + + size_t pos = 0; + while(!Input->eof()){ + pos = Input->getPos(); + Input->read(&c, 1); + if(c == Name[0]){ + if(Input->getSize() - Input->getPos() < Name.size())return 0; + + Input->read(&test[1], test.size()-1); + test[0] = c; + + if(test == Name){ + return pos; + } else { + Input->gotoPos(pos+1); + } + } + } + return 0; +} + +///============================================================================= +bool sm2obj::loadMeta(entityInfoStruct* Output, const std::string& Path, const std::string& Name){ + ffw::file input; + if(!input.open(Path, true, false, false)){ + LOG_ERROR("Failed to open meta file from: " + Path); + return false; + } + + LOG_DEBUG("Reading: " + Path); + + size_t pos = findName(&input, Name); + + if(pos == 0){ + LOG_DEBUG("Could not load attachments for blueprint: " + Name + " Blueprint might be outdated or there are no attachments!"); + return true; + } + + // Go back 6 bytes + size_t origin = pos-6; + input.gotoPos(origin); + + uint32_t totalEntries; + input.read(&totalEntries, sizeof(uint32_t)); + totalEntries = __builtin_bswap32(totalEntries); + + for(uint32_t i = 0; i < totalEntries; i++){ + std::string name0; + std::string name1; + std::string name2; + char bytes0[13]; + char additionalInformation[227]; + + uint16_t strLength; + input.read(&strLength, sizeof(uint16_t)); + strLength = __builtin_bswap16(strLength); + name0.resize(strLength); + input.read(&name0[0], strLength); + + input.read(&bytes0[0], sizeof(bytes0)); + + input.read(&strLength, sizeof(uint16_t)); + strLength = __builtin_bswap16(strLength); + name1.resize(strLength); + input.read(&name1[0], strLength); + + char unknownByte; + ffw::vec3i pos; + char orientation[12]; + input.read(&unknownByte, sizeof(unknownByte)); + input.read(&pos.x, sizeof(pos.x)); + input.read(&pos.y, sizeof(pos.y)); + input.read(&pos.z, sizeof(pos.z)); + pos.x = __builtin_bswap32(pos.x); + pos.y = __builtin_bswap32(pos.y); + pos.z = __builtin_bswap32(pos.z); + + input.read(&orientation, sizeof(orientation)); + + input.read(&strLength, sizeof(uint16_t)); + strLength = __builtin_bswap16(strLength); + name2.resize(strLength); + input.read(&name2[0], strLength); + + ffw::vec3i dockModule; + input.read(&unknownByte, sizeof(unknownByte)); + input.read(&dockModule.x, sizeof(dockModule.x)); + input.read(&dockModule.y, sizeof(dockModule.y)); + input.read(&dockModule.z, sizeof(dockModule.z)); + dockModule.x = __builtin_bswap32(dockModule.x); + dockModule.y = __builtin_bswap32(dockModule.y); + dockModule.z = __builtin_bswap32(dockModule.z); + + input.read(&additionalInformation[0], sizeof(additionalInformation)); + + size_t slash = name0.find('/'); + if(slash != std::string::npos){ + name0 = name0.substr(slash+1, name0.size()-slash-1); + } else { + LOG_ERROR("Failed to read next attachment path from meta: " + Path); + continue; + } + + ffw::quaternion quat; + ffw::vec3i dockOffset; + + if(orientation[4] == 0x0E && orientation[6] == 0x01){ + dockOffset = ffw::vec3i(0, 1, 0); + } + + if(orientation[4] == 0x0C && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, 1, 0); + } + + if(orientation[4] == 0x0F && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, 1, 0); + } + + if(orientation[4] == 0x0D && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, 1, 0); + } + + if(orientation[4] == 0x00 && orientation[6] == 0x00){ + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 0.0f, 1.0f); + dockOffset = ffw::vec3i(-1, 0, 0); + } + + if(orientation[4] == 0x03 && orientation[6] == 0x00){ + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 0.0f, 1.0f); + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(-1, 0, 0); + } + + if(orientation[4] == 0x02 && orientation[6] == 0x00){ + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 0.0f, 1.0f); + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(-1, 0, 0); + } + + if(orientation[4] == 0x01 && orientation[6] == 0x00){ + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 0.0f, 1.0f); + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(-1, 0, 0); + } + + if(orientation[4] == 0x04 && orientation[6] == 0x00){ + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 0.0f, 1.0f); + dockOffset = ffw::vec3i(1, 0, 0); + } + + if(orientation[4] == 0x07 && orientation[6] == 0x00){ + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 0.0f, 1.0f); + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(1, 0, 0); + } + + if(orientation[4] == 0x06 && orientation[6] == 0x00){ + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 0.0f, 1.0f); + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(1, 0, 0); + } + + if(orientation[4] == 0x05 && orientation[6] == 0x00){ + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 0.0f, 1.0f); + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(1, 0, 0); + } + + if(orientation[4] == 0x0B && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 0.0f, 1.0f); + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, -1, 0); + } + + if(orientation[4] == 0x08 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 0.0f, 1.0f); + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, -1, 0); + } + + if(orientation[4] == 0x09 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 0.0f, 1.0f); + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, -1, 0); + } + + if(orientation[4] == 0x0A && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 0.0f, 1.0f); + dockOffset = ffw::vec3i(0, -1, 0); + } + + if(orientation[4] == 0x00 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(270.0f, 1.0f, 0.0f, 0.0f); + dockOffset = ffw::vec3i(0, 0, 1); + } + + if(orientation[4] == 0x01 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(270.0f, 1.0f, 0.0f, 0.0f); + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, 0, 1); + } + + if(orientation[4] == 0x03 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(270.0f, 1.0f, 0.0f, 0.0f); + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, 0, 1); + } + + if(orientation[4] == 0x02 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(270.0f, 1.0f, 0.0f, 0.0f); + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, 0, 1); + } + + if(orientation[4] == 0x04 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(90.0f, 1.0f, 0.0f, 0.0f); + quat *= ffw::quaternion().rotate(180.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, 0, -1); + } + + if(orientation[4] == 0x05 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(90.0f, 1.0f, 0.0f, 0.0f); + quat *= ffw::quaternion().rotate(270.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, 0, -1); + } + + if(orientation[4] == 0x07 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(90.0f, 1.0f, 0.0f, 0.0f); + quat *= ffw::quaternion().rotate(90.0f, 0.0f, 1.0f, 0.0f); + dockOffset = ffw::vec3i(0, 0, -1); + } + + if(orientation[4] == 0x06 && orientation[6] == 0x01){ + quat *= ffw::quaternion().rotate(90.0f, 1.0f, 0.0f, 0.0f); + dockOffset = ffw::vec3i(0, 0, -1); + } + + Output->attachments.push_back({name2, "\\" + name0, (ffw::vec3f)pos, quat, (ffw::vec3f)dockModule, (ffw::vec3f)dockOffset, std::vector(), std::vector(), std::vector(), std::vector(), Output}); + } + + return true; +} diff --git a/source/exporter/loadMeta.h b/source/exporter/loadMeta.h new file mode 100644 index 0000000..dc0bb2c --- /dev/null +++ b/source/exporter/loadMeta.h @@ -0,0 +1,16 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SMD2OBJ_LOAD_META +#define SMD2OBJ_LOAD_META + +#include +#include "config.h" + +namespace sm2obj{ + bool loadMeta(entityInfoStruct* Output, const std::string& Path, const std::string& Name); +}; +#endif diff --git a/source/exporter/materialExport.cpp b/source/exporter/materialExport.cpp new file mode 100644 index 0000000..1370ca8 --- /dev/null +++ b/source/exporter/materialExport.cpp @@ -0,0 +1,68 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include "materialExport.h" +#include "config.h" + +//static ffw::file material; + +///============================================================================= +bool sm2obj::createMaterialAtlas(ffw::file* Output, bool UseDiffuse, bool UseNormal, bool UseAlpha, bool UseEmissive, bool Specular){ + Output->writeLine("# SM2OBJ\n"); + + for(int i = 0; i < 3; i++){ + Output->writeLine("newmtl Mat_Atlas_" + ffw::valToString(i)); + Output->writeLine("Ka 0.000 0.000 0.000"); // Ambient color + Output->writeLine("Kd 1.000 1.000 1.000"); // Diffuse color + if(Specular){ + Output->writeLine("Ks 0.800 0.800 0.800"); // Specular level + Output->writeLine("Ns 20.000"); // Glossiness + } else { + Output->writeLine("Ks 0.000 0.000 0.000"); // Specular level + Output->writeLine("Ns 0.000"); // Glossiness + } + Output->writeLine("illum 2"); // Specular on/off (1 = disabled, 2 = enabled) + Output->writeLine("Ni 1.000000"); + // Transparency (dissolved) + Output->writeLine("d 1.000000"); + // Textures + if(UseDiffuse) Output->writeLine("map_Kd Atlas_" + ffw::valToString(i) + "_diff." + config::imageExtension); + if(UseNormal) Output->writeLine("map_bump Atlas_" + ffw::valToString(i) + "_bump." + config::imageExtension); + if(UseAlpha) Output->writeLine("map_d Atlas_" + ffw::valToString(i) + "_alpha." + config::imageExtension); + if(UseEmissive)Output->writeLine("map_Ke Atlas_" + ffw::valToString(i) + "_emissive." + config::imageExtension); + Output->writeLine("\n"); + } + + return true; +} + +///============================================================================= +void sm2obj::createMaterialTile(ffw::file* Output, const blockInfoStruct* Block, int TextureIndex, bool UseDiffuse, bool UseNormal, bool UseAlpha, bool UseEmissive, bool Specular){ + Output->writeLine("newmtl Mat_" + Block->name + "_" + ffw::valToString(TextureIndex)); + Output->writeLine("Ka 0.000 0.000 0.000"); // Ambient color + Output->writeLine("Kd 1.000 1.000 1.000"); // Diffuse color + if(Specular){ + Output->writeLine("Ks 0.800 0.800 0.800"); // Specular level + Output->writeLine("Ns 20.000"); // Glossiness + } else { + Output->writeLine("Ks 0.000 0.000 0.000"); // Specular level + Output->writeLine("Ns 0.000"); // Glossiness + } + Output->writeLine("illum 2"); // Specular on/off (1 = disabled, 2 = enabled) + if(UseEmissive && Block->emissive) + // Emissive color + Output->writeLine("ke " + ffw::valToString(Block->light.r, 6) + " " + ffw::valToString(Block->light.g, 6) + + " " + ffw::valToString(Block->light.b, 6) + " " + ffw::valToString(Block->light.a, 6)); + + Output->writeLine("Ni 1.000000"); + // Transparency (dissolved) + Output->writeLine("d 1.000000"); + // Textures + if(UseDiffuse)Output->writeLine("map_Kd Tex_" + ffw::valToString(TextureIndex) + "_diff." + config::imageExtension); + if(UseNormal) Output->writeLine("map_bump Tex_" + ffw::valToString(TextureIndex) + "_bump." + config::imageExtension); + if(UseAlpha) Output->writeLine("map_d Tex_" + ffw::valToString(TextureIndex) + "_alpha." + config::imageExtension); + Output->writeLine("\n"); +} diff --git a/source/exporter/materialExport.h b/source/exporter/materialExport.h new file mode 100644 index 0000000..6b1416e --- /dev/null +++ b/source/exporter/materialExport.h @@ -0,0 +1,25 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_MATERIAL_EXPORT +#define SM2OBJ_MATERIAL_EXPORT + +#include +#include "config.h" +#include "blockConfig.h" + +namespace sm2obj{ + bool createMaterialAtlas(ffw::file* Output, bool UseDiffuse, bool UseNormal, bool UseAlpha, bool UseEmissive, bool Specular); + //bool beginMaterialExport(); + void createMaterialTile(ffw::file* Output, const blockInfoStruct* block, int TextureIndex, bool UseDiffuse, bool UseNormal, bool UseAlpha, bool UseEmissive, bool Specular); + //void endMaterialExport(); +} + +#endif + + + + diff --git a/source/polygonFiltering.cpp b/source/exporter/polygonFiltering.cpp similarity index 72% rename from source/polygonFiltering.cpp rename to source/exporter/polygonFiltering.cpp index 49d176f..b894064 100644 --- a/source/polygonFiltering.cpp +++ b/source/exporter/polygonFiltering.cpp @@ -1,24 +1,21 @@ /* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License */ #include "polygonFiltering.h" #include "blockConfig.h" ///============================================================================= -bool removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output){ +bool sm2obj::removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output){ int processedFaces = 0; for(uint32_t i = 0; i < Input->indicesCount; i++){ if(Input->indices[i].w < 0){ - ffw::vec3f v0(Input->vertices[Input->indices[i].x]); - ffw::vec3f v1(Input->vertices[Input->indices[i].y]); - ffw::vec3f v2(Input->vertices[Input->indices[i].z]); - - ffw::vec3f c = (v0 + v1 + v2) / 3.0f; + ffw::vec3f c = (Input->vertices[Input->indices[i].x] + Input->vertices[Input->indices[i].y] + Input->vertices[Input->indices[i].z]) / 3.0f; bool test = true; bool targetTransparency = false; @@ -27,11 +24,7 @@ bool removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output){ for(uint32_t t = 0; t < Input->indicesCount; t++){ if(t == i)continue; - ffw::vec3f vt0(Input->vertices[Input->indices[t].x]); - ffw::vec3f vt1(Input->vertices[Input->indices[t].y]); - ffw::vec3f vt2(Input->vertices[Input->indices[t].z]); - - ffw::vec3f ct = (vt0 + vt1 + vt2) / 3.0f; + ffw::vec3f ct = (Input->vertices[Input->indices[t].x] + Input->vertices[Input->indices[t].y] + Input->vertices[Input->indices[t].z]) / 3.0f; if(ffw::vec3f(c - ct).length() < 0.2){ targetTransparency = Input->indicesMat[t][2] & 0x00FF; @@ -54,9 +47,9 @@ bool removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output){ // The tested polygon does not occupies same space with any other polygon // Add it to output if(test){ - Output->vertices[Output->verticesCount + 0] = v0; - Output->vertices[Output->verticesCount + 1] = v1; - Output->vertices[Output->verticesCount + 2] = v2; + Output->vertices[Output->verticesCount + 0] = Input->vertices[Input->indices[i].x]; + Output->vertices[Output->verticesCount + 1] = Input->vertices[Input->indices[i].y]; + Output->vertices[Output->verticesCount + 2] = Input->vertices[Input->indices[i].z]; Output->verticesCount += 3; Output->indices[Output->indicesCount] = ffw::vec4i( processedFaces+0, processedFaces+1, processedFaces+2, -1); @@ -64,17 +57,12 @@ bool removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output){ Output->indicesMat[Output->indicesCount][0] = Input->indicesMat[i][0]; Output->indicesMat[Output->indicesCount][1] = Input->indicesMat[i][1]; processedFaces += 3; - //resultIndiceMat[resultIndicesCount] = indiceMatIn[i]; Output->indicesCount += 1; } } else { - ffw::vec3f v0(Input->vertices[Input->indices[i].x]); - ffw::vec3f v1(Input->vertices[Input->indices[i].y]); - ffw::vec3f v2(Input->vertices[Input->indices[i].z]); - ffw::vec3f v3(Input->vertices[Input->indices[i].w]); - ffw::vec3f c = (v0 + v1 + v2 + v3) / 4.0f; + ffw::vec3f c = (Input->vertices[Input->indices[i].x] + Input->vertices[Input->indices[i].y] + Input->vertices[Input->indices[i].z] + Input->vertices[Input->indices[i].w]) / 4.0f; bool test = true; bool targetTransparency = false; @@ -84,12 +72,7 @@ bool removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output){ if(Input->indices[t].w == -1)continue; - ffw::vec3f vt0(Input->vertices[Input->indices[t].x]); - ffw::vec3f vt1(Input->vertices[Input->indices[t].y]); - ffw::vec3f vt2(Input->vertices[Input->indices[t].z]); - ffw::vec3f vt3(Input->vertices[Input->indices[t].w]); - - ffw::vec3f ct = (vt0 + vt1 + vt2 + vt3) / 4.0f; + ffw::vec3f ct = (Input->vertices[Input->indices[t].x] + Input->vertices[Input->indices[t].y] + Input->vertices[Input->indices[t].z] + Input->vertices[Input->indices[t].w]) / 4.0f; if(ffw::vec3f(c - ct).length() < 0.2){ targetTransparency = Input->indicesMat[t][2] & 0x00FF; @@ -110,10 +93,10 @@ bool removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output){ } if(test){ - Output->vertices[Output->verticesCount + 0] = v0; - Output->vertices[Output->verticesCount + 1] = v1; - Output->vertices[Output->verticesCount + 2] = v2; - Output->vertices[Output->verticesCount + 3] = v3; + Output->vertices[Output->verticesCount + 0] = Input->vertices[Input->indices[i].x]; + Output->vertices[Output->verticesCount + 1] = Input->vertices[Input->indices[i].y]; + Output->vertices[Output->verticesCount + 2] = Input->vertices[Input->indices[i].z]; + Output->vertices[Output->verticesCount + 3] = Input->vertices[Input->indices[i].w]; Output->verticesCount += 4; Output->indices[Output->indicesCount] = ffw::vec4i( processedFaces+0, processedFaces+1, processedFaces+2, processedFaces+3); @@ -131,7 +114,7 @@ bool removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output){ } ///============================================================================= -bool removeDuplicatedVertices(chunkBufferStruct* Input, chunkBufferStruct* Output){ +bool sm2obj::removeDuplicatedVertices(chunkBufferStruct* Input, chunkBufferStruct* Output){ for(uint32_t i = 0; i < Input->indicesCount; i++){ ffw::vec4i result; for(int v = 0; v < 4; v++){ @@ -158,8 +141,6 @@ bool removeDuplicatedVertices(chunkBufferStruct* Input, chunkBufferStruct* Outpu if(test == -1){ Output->vertices[Output->verticesCount] = vertex; - //Output->indices[Output->indicesCount] = index; - //Output->indicesCount++; if(v == 0)result.x = Output->verticesCount; if(v == 1)result.y = Output->verticesCount; if(v == 2)result.z = Output->verticesCount; diff --git a/source/exporter/polygonFiltering.h b/source/exporter/polygonFiltering.h new file mode 100644 index 0000000..7ab506e --- /dev/null +++ b/source/exporter/polygonFiltering.h @@ -0,0 +1,20 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_POLYGON_FILTERING +#define SM2OBJ_POLYGON_FILTERING + +#include +#include "config.h" + +namespace sm2obj{ + bool removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output); + bool removeDuplicatedVertices(chunkBufferStruct* Input, chunkBufferStruct* Output); +} + +#endif + + diff --git a/source/exporter/structures.h b/source/exporter/structures.h new file mode 100644 index 0000000..0f5306b --- /dev/null +++ b/source/exporter/structures.h @@ -0,0 +1,76 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SMD2OBJ_STRUCTURES +#define SMD2OBJ_STRUCTURES + +namespace sm2obj{ + struct chunkBufferStruct { + ffw::vec3f vertices [SM2OBJ_CHUNK_SIZE * SM2OBJ_CHUNK_MAX_VERTICES]; + ffw::vec4i indices [SM2OBJ_CHUNK_SIZE * SM2OBJ_CHUNK_MAX_INDICES]; + ffw::vec4i indicesUvs[SM2OBJ_CHUNK_SIZE * SM2OBJ_CHUNK_MAX_INDICES]; + uint16_t indicesMat [SM2OBJ_CHUNK_SIZE * SM2OBJ_CHUNK_MAX_INDICES][3]; + uint32_t indicesCount; + uint32_t verticesCount; + uint32_t indicesOffset; + std::vector extractedMaterialsList; + }; + + // Information about chunks + struct chunkInfoStruct { + // Relative position + ffw::vec3i pos; + // Relative position of the file from which chunk was loaded + ffw::vec3i posFile; + // Index of the file + int fileIndex; + }; + + // Information about entity + struct entityInfoStruct { + // Name + std::string name; + // Relative path + std::string path; + // Position of docking in parent space + ffw::vec3f pos; + // Orientation of docking + ffw::quaternion orientation; + // Position of dock module in local space + ffw::vec3f dockModule; + // Dock offset in local space + ffw::vec3f dockOffset; + // Attached ships and turrets + std::vector attachments; + // Chunk information + std::vector chunks; + // Extracted tiles + std::vector extractedTiles; + // Extracted tiles + std::vector extractedMaterials; + // Parent pointer + entityInfoStruct* parent; + }; + + // Information about thread + struct threadInfoStruct { + // Path to raw chunks + std::string tempFolder; + // Main thread class + ffw::thread thread; + // Lock mutex + ffw::mutex mut; + // Chunk information that is beeing processed by thread + chunkInfoStruct* chunkPtr; + // Buffers to save chunk data + chunkBufferStruct chunkBufferA; + chunkBufferStruct chunkBufferB; + // Pointer to the entity + entityInfoStruct* entityPtr; + }; +}; + +#endif diff --git a/source/exporter/textureExport.cpp b/source/exporter/textureExport.cpp new file mode 100644 index 0000000..6efcc63 --- /dev/null +++ b/source/exporter/textureExport.cpp @@ -0,0 +1,336 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include "textureExport.h" +#include "blockConfig.h" +#include + +static std::string textures[6] = {"t000.png", "t001.png", "t002.png", "t000_NRM.png", "t001_NRM.png", "t002_NRM.png"}; + +static bool loadBlocks(const std::string& InputConfigFolder){ + sm2obj::clearBlockConfig(); + // Load block types + if(!sm2obj::loadBlockTypes(InputConfigFolder + "\\BlockTypes.properties")){ + TERMINATE(false); + } + + // Load block config + if(!sm2obj::loadBlockConfig(InputConfigFolder + "\\BlockConfig.xml")){ + TERMINATE(false); + } + return true; +} + +///============================================================================= +static void extractAlpha(unsigned char* InputPixels, int InputWidth, int InputHeight, unsigned char* OutputPixels){ + for(int y = 0; y < InputHeight; y++){ + for(int x = 0; x < InputWidth; x++){ + OutputPixels[InputWidth*y +x] = InputPixels[InputWidth*y*4 + x*4 +3]; + } + } +} + +///============================================================================= +static void subsection(unsigned char* InputPixels, int InputWidth, int InputHeight, int OutputPosX, int OutputPosY, int InputChannels, unsigned char* OutputPixels, int OutputWidth, int OutputHeight){ + for(int y = 0, p = 0; y < InputHeight; y++){ + if(y >= OutputPosY && y < OutputPosY+OutputHeight){ + memcpy(&OutputPixels[OutputWidth*p*InputChannels], &InputPixels[InputWidth*(InputHeight - y -1)*InputChannels + OutputPosX*InputChannels], OutputWidth*InputChannels); + p++; + } + } +} + +///============================================================================= +static void convertNormalToBump(unsigned char* InputPixels, int InputWidth, int InputHeight, unsigned char* OutputPixels){ + ffw::vec3f n(0.0f, 0.0f, 1.0f); + for(int y = 0; y < InputHeight; y++){ + for(int x = 0; x < InputWidth; x++){ + //OutputPixels[InputWidth*y +x] = InputPixels[InputWidth*y*3 + x*3 +0]; + ffw::vec3f norm(InputPixels[InputWidth*y*3 + x*3 +0] / 255.0f, InputPixels[InputWidth*y*3 + x*3 +1] / 255.0f, InputPixels[InputWidth*y*3 + x*3 +2] / 255.0f); + + float dot = ffw::dot(n, norm); + dot = powf(dot, 20.0f); + OutputPixels[InputWidth*y +x] = uint8_t(dot*255.0f); + } + } +} + +///============================================================================= +bool sm2obj::exportEmissiveAtlas(const std::string& InputConfigFolder, const std::string& OutputFolder, int TileWidth){ + LOG_DEBUG("Exporting emissive atlas"); + + if(!loadBlocks(InputConfigFolder))return false; + + int index = 0; + unsigned char* pixels = new unsigned char[TileWidth*16*TileWidth*16*3]; + int width = TileWidth*16; + + for(int i = 0; i < 3; i++){ + + for(int p = 0; p < TileWidth*16*TileWidth*16*3; p++)pixels[p] = 0; + + for(int y = 15; y >= 0; y--){ + for(int x = 0; x < 16; x++){ + const blockInfoStruct* block = findBlockByTexture(index); + if(block != NULL && block->emissive){ + + for(int h = 0; h < TileWidth; h++){ + for(int w = 0; w < TileWidth; w++){ + pixels[width*(y*TileWidth+h)*3 + (x*TileWidth+w)*3 +0] = block->light.r * 255; + pixels[width*(y*TileWidth+h)*3 + (x*TileWidth+w)*3 +1] = block->light.g * 255; + pixels[width*(y*TileWidth+h)*3 + (x*TileWidth+w)*3 +2] = block->light.b * 255; + } + } + + } + index++; + } + } + + if(!config::imageSaverFunc(OutputFolder + "\\Atlas_" + ffw::valToString(i) + "_emissive." + config::imageExtension, pixels, TileWidth*16, TileWidth*16, ffw::imageType::RGB_888)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Atlas_" + ffw::valToString(i) + "_emissive." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + delete[] pixels; + return true; +} + +///============================================================================= +bool sm2obj::exportAtlases(const std::string& InputFolder, const std::string& OutputFolder, bool ConvertNormals){ + int index = 0; + + LOG_DEBUG("Exporting textures from: " + InputFolder); + + int total = 6; + int progress = 1; + + for(int i = 0; i < 6; i++){ + std::string tex = textures[i]; + if(i == 3)index = 0; + unsigned char* pixels; + int width; + int height; + int channels; + ffw::imageType type; + + LOG_DEBUG("Reading file: " + InputFolder + "\\" + tex); + + if(!ffw::loadPNG(InputFolder + "\\" + tex, &pixels, &width, &height, &type)){ + LOG_ERROR("Failed to load texture: " + InputFolder + "\\" + tex); + TERMINATE(false); + } + + if(width != height){ + LOG_ERROR("Failed to load texture: " + InputFolder + "\\" + tex + " Texture must have same width and height!"); + TERMINATE(false); + } + + if(!(width == 1024 || width == 2048 || width == 4096)){ + LOG_ERROR("Failed to load texture: " + InputFolder + "\\" + tex + " Wrong texture size! Must be either 1024, 2048, or 4096!"); + TERMINATE(false); + } + + if(type == ffw::imageType::RGB_888)channels = 3; + else if(type == ffw::imageType::RGB_ALPHA_8888)channels = 4; + else { + LOG_ERROR("Failed to load texture: " + InputFolder + "\\" + tex + " Wrong texture format! Must be either 8-bit RGB or 8-bit RGBA!"); + TERMINATE(false); + } + + unsigned char* outputAlphaPixels = new unsigned char[width*height*1]; + unsigned char* outputBumpPixels = new unsigned char[width*height*1]; + + bool isNormal = false; + if(tex.find("_NRM") != std::string::npos){ + isNormal = true; + } + + LOG_INFO("Processing texture: " + tex); + + PROGRESS(progress, total); + + // Diffuse + if(!isNormal){ + if(!config::imageSaverFunc(OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_diff." + config::imageExtension, pixels, width, height, type)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_diff." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + // Alpha + if(!isNormal && type == ffw::imageType::RGB_ALPHA_8888){ + extractAlpha(pixels, width, height, outputAlphaPixels); + + if(!config::imageSaverFunc(OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_alpha." + config::imageExtension, outputAlphaPixels, width, height, ffw::imageType::GRAYSCALE_8)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_alpha." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + + // Alpha alternative + } else if(!isNormal){ + for(int p = 0; p < width*height*1; p++)outputAlphaPixels[p] = 255; + + if(!config::imageSaverFunc(OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_alpha." + config::imageExtension, outputAlphaPixels, width, height, ffw::imageType::GRAYSCALE_8)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_alpha." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + // Normals + if(isNormal && !ConvertNormals){ + if(!config::imageSaverFunc(OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_bump." + config::imageExtension, pixels, width, height, type)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_bump." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + // Bump + if(isNormal && ConvertNormals){ + convertNormalToBump(pixels, width, height, outputBumpPixels); + + if(!config::imageSaverFunc(OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_bump." + config::imageExtension, outputBumpPixels, width, height, ffw::imageType::GRAYSCALE_8)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Atlas_" + ffw::valToString(index) + "_bump." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + index++; + progress++; + + delete[] pixels; + delete[] outputAlphaPixels; + delete[] outputBumpPixels; + } + + TERMINATE(true); + return true; +} + +///============================================================================= +bool sm2obj::exportTextures(const std::string& InputFolder, const std::string& OutputFolder, bool ConvertNormals){ + int index = 0; + + LOG_DEBUG("Exporting textures from: " + InputFolder); + + int total = 16*16*6; + int progress = 1; + + for(int i = 0; i < 6; i++){ + std::string tex = textures[i]; + if(i == 3)index = 0; + unsigned char* pixels; + int width; + int height; + int channels; + ffw::imageType type; + + LOG_DEBUG("Reading file: " + InputFolder + "\\" + tex); + + if(!ffw::loadPNG(InputFolder + "\\" + tex, &pixels, &width, &height, &type)){ + LOG_ERROR("Failed to load texture: " + InputFolder + "\\" + tex); + TERMINATE(false); + } + + if(width != height){ + LOG_ERROR("Failed to load texture: " + InputFolder + "\\" + tex + " Texture must have same width and height!"); + TERMINATE(false); + } + + if(!(width == 1024 || width == 2048 || width == 4096)){ + LOG_ERROR("Failed to load texture: " + InputFolder + "\\" + tex + " Wrong texture size! Must be either 1024, 2048, or 4096!"); + TERMINATE(false); + } + + if(type == ffw::imageType::RGB_888)channels = 3; + else if(type == ffw::imageType::RGB_ALPHA_8888)channels = 4; + else { + LOG_ERROR("Failed to load texture: " + InputFolder + "\\" + tex + " Wrong texture format! Must be either 8-bit RGB or 8-bit RGBA!"); + TERMINATE(false); + } + + int bleeding = (width / 16)*0.15625f; + + int splitSize = (width / 16) - bleeding; + LOG_DEBUG("Splitting texture to: " + ffw::valToString(splitSize) + "x" + ffw::valToString(splitSize) + " chunks"); + + unsigned char* outputDiffusePixels = new unsigned char[splitSize*splitSize*channels]; + unsigned char* outputAlphaPixels = new unsigned char[splitSize*splitSize*1]; + unsigned char* outputBumpPixels = new unsigned char[splitSize*splitSize*1]; + + bool isNormal = false; + if(tex.find("_NRM") != std::string::npos){ + isNormal = true; + } + + LOG_INFO("Processing texture: " + tex); + + for(int y = 0; y < height; y += (width / 16)){ + for(int x = 0; x < width; x += (width / 16)){ + PROGRESS(progress, total); + + subsection(pixels, width, height, x + bleeding/2, y + bleeding/2, channels, outputDiffusePixels, splitSize, splitSize); + + // Diffuse + if(!isNormal){ + if(!config::imageSaverFunc(OutputFolder + "\\Tex_" + ffw::valToString(index) + "_diff." + config::imageExtension, outputDiffusePixels, splitSize, splitSize, type)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Tex_" + ffw::valToString(index) + "_diff." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + // Alpha + if(!isNormal && type == ffw::imageType::RGB_ALPHA_8888){ + extractAlpha(outputDiffusePixels, splitSize, splitSize, outputAlphaPixels); + + if(!config::imageSaverFunc(OutputFolder + "\\Tex_" + ffw::valToString(index) + "_alpha." + config::imageExtension, outputAlphaPixels, splitSize, splitSize, ffw::imageType::GRAYSCALE_8)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Tex_" + ffw::valToString(index) + "_alpha." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + // Alpha alternative + if(!isNormal && type == ffw::imageType::RGB_ALPHA_8888){ + for(int p = 0; p < splitSize*splitSize*1; p++)outputAlphaPixels[p] = 255; + + if(!config::imageSaverFunc(OutputFolder + "\\Tex_" + ffw::valToString(index) + "_alpha." + config::imageExtension, outputAlphaPixels, splitSize, splitSize, ffw::imageType::GRAYSCALE_8)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Tex_" + ffw::valToString(index) + "_alpha." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + // Normals + if(isNormal && !ConvertNormals){ + if(!config::imageSaverFunc(OutputFolder + "\\Tex_" + ffw::valToString(index) + "_bump." + config::imageExtension, outputDiffusePixels, splitSize, splitSize, type)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Tex_" + ffw::valToString(index) + "_bump." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + // Bump + if(isNormal && ConvertNormals){ + convertNormalToBump(outputDiffusePixels, splitSize, splitSize, outputBumpPixels); + + if(!config::imageSaverFunc(OutputFolder + "\\Tex_" + ffw::valToString(index) + "_bump." + config::imageExtension, outputBumpPixels, splitSize, splitSize, ffw::imageType::GRAYSCALE_8)){ + LOG_ERROR("Failed to save texture: " + OutputFolder + "\\Tex_" + ffw::valToString(index) + "_bump." + config::imageExtension + " Program might not have permissions or target folder " + OutputFolder + " does not exists!"); + TERMINATE(false); + } + } + + index++; + progress++; + } + } + + delete[] outputDiffusePixels; + delete[] outputAlphaPixels; + delete[] outputBumpPixels; + delete[] pixels; + } + TERMINATE(true); + return true; +} diff --git a/source/exporter/textureExport.h b/source/exporter/textureExport.h new file mode 100644 index 0000000..37583b1 --- /dev/null +++ b/source/exporter/textureExport.h @@ -0,0 +1,22 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_TEXTURE_EXPORT +#define SM2OBJ_TEXTURE_EXPORT + +#include +#include "config.h" + +namespace sm2obj{ + bool exportEmissiveAtlas(const std::string& InputConfigFolder, const std::string& OutputFolder, int TileWidth); + bool exportTextures(const std::string& InputFolder, const std::string& OutputFolder, bool ConvertNormals); + bool exportAtlases(const std::string& InputFolder, const std::string& OutputFolder, bool ConvertNormals); +}; + +#endif + + + diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..a3551a1 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,14 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include "window.h" + +static sm2obj::window mainWindow; + +int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow){ + ffw::logger::initLogger(ffw::getExecutablePath()); + return mainWindow.create(hThisInstance, nCmdShow); +} diff --git a/source/mainExporter.cpp b/source/mainExporter.cpp deleted file mode 100644 index 61202b6..0000000 --- a/source/mainExporter.cpp +++ /dev/null @@ -1,529 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#include -#include - -#include "blockConfig.h" -#include "chunkHeader.h" -#include "chunkLoader.h" -#include "chunkTempLoader.h" -#include "config.h" -#include "blockConstructor.h" -#include "blockExtractor.h" -#include "polygonFiltering.h" -#include "textureExport.h" -#include "materialExport.h" -#include "blockVerticeData.h" - -#define TERMINATE(X)\ -if(exportExitFunc != NULL)exportExitFunc(X); return (void*)X; - -static ffw::vec3i boundingBoxMin; -static ffw::vec3i boundingBoxMax; -static int fileChunksTotal = 0; -static int processedChunks = 0; -static std::string executablePath; - -// Information about chunks -struct chunkInfoStruct { - // Relative position - ffw::vec3i pos; - // Relative position of the file from which chunk was loaded - ffw::vec3i posFile; - // Index of the chunk - int index; - // Index of the file - int indexFile; -}; - -// Information about thread -struct threadInfoStruct { - // Main thread class - ffw::thread thread; - // Lock mutex - ffw::mutex mut; - // Chunk information that is beeing processed by thread - chunkInfoStruct* chunkPtr; - // Buffers to save chunk data - chunkBufferStruct chunkBufferA; - chunkBufferStruct chunkBufferB; -}; - -static threadInfoStruct* threads; -static std::vector chunkInfo; - -///============================================================================= -void progress(int Value, int Range){ - if(exportProgressFunc != NULL)exportProgressFunc(Value, Range); -} - -///============================================================================= -void* processChunk(void* ThreadInfoPtr){ - // Get pointer to the thread info struct - threadInfoStruct* threadInfo = static_cast(ThreadInfoPtr); - // Lock this thread - threadInfo->mut.lock(); - // Get chunk pointer - chunkInfoStruct* chunk = threadInfo->chunkPtr; - - // Load chunk from file - uint32_t chunkData[16][16][16]; - if(!loadTempChunk(executablePath, chunk->index, chunk->indexFile, &chunkData[0][0][0])){ - ffw::logger().error() << "Error loading chunk index: " << chunk->index << " file: " << chunk->indexFile; - threadInfo->mut.unlock(); - return NULL; - } - - // Reset buffer A - threadInfo->chunkBufferA.indicesCount = 0; - threadInfo->chunkBufferA.verticesCount = 0; - threadInfo->chunkBufferA.indicesOffset = 0; - - // Build blocks by looking through all XYZ positions in chunk - ffw::vec3i posRel; - for(posRel.z = 0; posRel.z < 16; posRel.z++){ - for(posRel.y = 0; posRel.y < 16; posRel.y++){ - for(posRel.x = 0; posRel.x < 16; posRel.x++){ - if(chunkData[posRel.z][posRel.y][posRel.x] == 0)continue; - - buildBlock(posRel, chunk->pos, chunk->posFile, chunkData, &threadInfo->chunkBufferA); - } - } - } - - // Reset buffer B - threadInfo->chunkBufferB.indicesCount = 0; - threadInfo->chunkBufferB.verticesCount = 0; - threadInfo->chunkBufferB.indicesOffset = 0; - - // Filter polygons that occupies same space - removeDuplicatedFaces(&threadInfo->chunkBufferA, &threadInfo->chunkBufferB); - - // Now the data is in buffer B - // We will use buffer A as a output - threadInfo->chunkBufferA.indicesCount = 0; - threadInfo->chunkBufferA.verticesCount = 0; - threadInfo->chunkBufferA.indicesOffset = 0; - - // Remove duplicated vertices - removeDuplicatedVertices(&threadInfo->chunkBufferB, &threadInfo->chunkBufferA); - - // Now the data is in buffer A - // Save it to temp file - saveRawBlocks(executablePath, &threadInfo->chunkBufferA, chunk->index, chunk->indexFile); - - ffw::logger().debug() << "Chunk: " << chunk->index << " file: " << chunk->indexFile << " exported!"; - - // Unlock this thread - threadInfo->mut.unlock(); - return NULL; -} - -///============================================================================= -void* runExporter(void* DATA){ - chunkInfo.clear(); - fileChunksTotal = 0; - - // Initialize logger - ffw::logger::initLogger(); - - ffw::logger().print() << "Starting exporter..."; - - // Get executable path - executablePath = ffw::getExecutablePath(); - - // Check if there is a job to do - if(!textureExport && filePath.size() == 0){ - ffw::logger().error() << "Nothing to do..."; - TERMINATE(false); - } - - // Check if path to StarMade data folder is not empty - if(starMadeDataFolder.size() == 0){ - ffw::logger().error() << "Wrong StarMade data folder! Have you missed something?"; - TERMINATE(false); - } - - // Check if target file is not empty - if(filePath.size() == 0){ - ffw::logger().error() << "Wrong file name! Have you missed something?"; - TERMINATE(false); - } - - // Check if output file name is not empty - if(fileName.size() == 0){ - ffw::logger().error() << "Wrong file name! Have you missed something?"; - TERMINATE(false); - } - - // Check if file exists - if(!ffw::checkIfFileExists(filePath)){ - ffw::logger().error() << "Invalid path to blueprint! Maybe the program does not have permissions..."; - TERMINATE(false); - } - - // Separate path to folder, name, and to extension - std::string blueprintFolder; - std::string blueprintName; - std::string blueprintExtension; - ffw::getFilePathProperties(filePath, &blueprintFolder, &blueprintName, &blueprintExtension); - - // Get frist part of the blueprint file name - // this: "Isanth Type-Zero Mb.0.0.0" becames this: "Isanth Type-Zero Mb" - std::string blueprintNameShort; - size_t dot = blueprintName.find('.'); - if(dot == std::string::npos){ - ffw::logger().error() << "Selected file has invalid name..."; - TERMINATE(false); - } else { - blueprintNameShort = blueprintName.substr(0, dot); - } - - // Check for all files inside blueprint folder - // We need to include all files - std::vector allFiles; - allFiles.push_back(blueprintName + ".smd2"); - - // Open target directory - ffw::listDirectory dir; - if(!dir.open(blueprintFolder)){ - ffw::logger().error() << "Can not look into the blueprint folder! Maybe the program does not have permissions..."; - TERMINATE(false); - } - - // Open output OBJ file - ffw::file objOutput; - if(!objOutput.open(fileOutputFolder + "\\" + fileName + ".obj", false, true, false)){ - ffw::logger().error() << "Error failed to open OBJ for writing!"; - TERMINATE(false); - } - - // Include all files that match blueprint name - for(const auto& file : dir.getFiles()){ - if(file == allFiles[0])continue; - if(file.find(blueprintNameShort) == 0){ - ffw::logger().debug() << "Adding blueprint file: " << file; - allFiles.push_back(file); - } - } - - // If no output folder is defined then the default path is C:/ - if(fileOutputFolder.size() == 0)fileOutputFolder = "C:"; - - // Check file extension - if(blueprintExtension != "smd2"){ - ffw::logger().error() << "Wrong blueprint file! Expected a file with *.smd2 extension"; - TERMINATE(false); - } - - // Begin material export - if(materialExport){ - if(textureSplit && !beginMaterialExport()){ - TERMINATE(false); - } else if(!textureSplit && !createMaterialAtlas()){ - TERMINATE(false); - } - resetMaterials(); - ffw::logger().print() << "Exporting materials is enabled!"; - } - - // Print some debug information - ffw::logger().print() << "Starting with " << threadsCount << " threads!"; - ffw::logger().print() << "Target blueprint: " << blueprintFolder; - - // Create target directories - ffw::createDirectory(executablePath + "\\temp"); - ffw::createDirectory(fileOutputFolder); - - // Load block config - if(!loadBlockTypes(starMadeDataFolder + "\\config\\BlockTypes.properties")){ - ffw::logger().error() << "Failed to load block types from \"data\\BlockTypes.properties\" Check if file exists!"; - TERMINATE(false); - } else { - ffw::logger().print() << "Block types loaded!"; - } - - if(!loadBlockConfig(starMadeDataFolder + "\\config\\BlockConfig.xml")){ - ffw::logger().error() << "Failed to load block config from \"data\\BlockConfig.xml\" Check if file exists!"; - TERMINATE(false); - } else { - ffw::logger().print() << "Block config loaded!"; - } - - // Run texture export - if(textureExport && textureSplit){ - ffw::logger().print() << "Exporting textures..."; - ffw::createDirectory(fileOutputFolder + "\\textures"); - if(!exportTextures(starMadeDataFolder + "\\textures\\block\\Default\\256")){ - TERMINATE(false); - } - } else if(textureExport){ - ffw::logger().print() << "Exporting atlases..."; - ffw::createDirectory(fileOutputFolder + "\\atlases"); - if(!exportAtlases(starMadeDataFolder + "\\textures\\block\\Default\\256")){ - TERMINATE(false); - } - } - - ffw::logger().print() << "Exporting blueprint..."; - - // Load all files - int fileIndex = 0; - for(const auto& file : allFiles){ - ffw::file fileInput; - - ffw::logger().print() << "Reading file: " << file; - - // Get file position from file name - size_t pos = file.find('.'); - if(pos == std::string::npos){ - ffw::logger().error() << "Error while retrieving file position from file name! Expected dot with coordinates!"; - TERMINATE(false); - } - - // Get position as tokens - std::vector tokens = ffw::getTokens(file.substr(pos+1, file.size()-pos-6), '.'); - if(tokens.size() != 3){ - ffw::logger().error() << "Error while retrieving file position from file name! Wrong coordinates!"; - TERMINATE(false); - } - - // Convert position to integers - ffw::vec3i filePos; - try { - filePos.x = ffw::stringToVal(tokens[0]); - filePos.y = ffw::stringToVal(tokens[0]); - filePos.z = ffw::stringToVal(tokens[0]); - filePos *= 256; - } catch (std::exception& e){ - ffw::logger().error() << "Error while retrieving file position from file name! Wrong coordinates!"; - } - - // Print file position - ffw::logger().debug() << "File position: " << filePos; - - // Open a file - if(!fileInput.open(blueprintFolder + "\\" + file, true, false, false)){ - ffw::logger().error() << "Failed to open file: " << file; - TERMINATE(false); - } - - // Check if file is big enough to contain header - size_t fileSize = fileInput.getSize(); - if(fileSize < size_t(4+32768+16384)){ - ffw::logger().error() << "File is invalid! File is too small. Expected at least 49156 bytes."; - TERMINATE(false); - } - - // Load header - int chunkIndex[CHUNK_SIZE]; - int totalChunks = 0; - if(!loadChunkHeader(&fileInput, &chunkIndex[0], &totalChunks)){ - //ffw::logger().error() << "Failed to load chunk header!"; - TERMINATE(false); - } - - // Remember the file offset - size_t fileOffset = fileInput.getPos(); - - ffw::logger().debug() << "Total chunks: " << totalChunks; - fileChunksTotal += totalChunks; - - // Sort indexes - // We need to load chunks ordered - std::sort(std::begin(chunkIndex), std::end(chunkIndex)); - - // Load all chunks - uint32_t chunkData[16][16][16]; - ffw::vec3i chunkPos; - for(int i = 0; i < CHUNK_SIZE; i++){ - if(chunkIndex[i] < 0)continue; - ffw::logger().debug() << "Loading chunk index: " << chunkIndex[i]; - - // Load chunk - fileInput.gotoPos(fileOffset + chunkIndex[i]*5120); - if(!loadChunk(&fileInput, fileOffset, chunkIndex[i], chunkData, &chunkPos)){ - continue; - } - - // Calculate bounding box - if(chunkPos.x < boundingBoxMin.x)boundingBoxMin.x = chunkPos.x; - if(chunkPos.x+16 > boundingBoxMax.x)boundingBoxMax.x = chunkPos.x+16; - - if(chunkPos.y < boundingBoxMin.y)boundingBoxMin.y = chunkPos.y; - if(chunkPos.y+16 > boundingBoxMax.y)boundingBoxMax.y = chunkPos.y+16; - - if(chunkPos.z < boundingBoxMin.z)boundingBoxMin.z = chunkPos.z; - if(chunkPos.z+16 > boundingBoxMax.z)boundingBoxMax.z = chunkPos.z+16; - - // Save decompressed raw chunk data to temp folder - if(!saveTempChunk(executablePath, chunkIndex[i], fileIndex, &chunkData[0][0][0])){ - ffw::logger().warning() << "Error while saving chunk to temp!"; - continue; - } - - // All chunk to list - chunkInfo.push_back(chunkInfoStruct{chunkPos, filePos, chunkIndex[i], fileIndex}); - } - - fileIndex++; - } - - ffw::logger().print() << "Bounding box -X: " << boundingBoxMin.x << " +X: " << boundingBoxMax.x; - ffw::logger().print() << "Bounding box -Y: " << boundingBoxMin.y << " +Y: " << boundingBoxMax.y; - ffw::logger().print() << "Bounding box -Z: " << boundingBoxMin.z << " +Z: " << boundingBoxMax.z; - - // Create threads - threads = new threadInfoStruct[threadsCount]; - for(int i = 0; i < threadsCount; i++){ - threads[i].thread.bindFunction(&processChunk); - } - - // Start exporting... - ffw::logger().print() << "Starting threads..."; - - // export chunks one by one - processedChunks = 0; - for(auto& chunk : chunkInfo){ - // Find an empty thread - while(true){ - bool found = false; - // Loop through all threads - for(int i = 0; i < threadsCount; i++){ - // We can lock a thread only of it has finished - if(threads[i].mut.tryLock()){ - // We found an empty thread, bind information and run it - processedChunks++; - progress(processedChunks, fileChunksTotal); - ffw::logger().print() << "Processing: " << processedChunks << " out of: " << fileChunksTotal; - // Unlock mutex - threads[i].mut.unlock(); - // Always join even if thread has stopped - threads[i].thread.join(); - // Bind chunk - threads[i].chunkPtr = &chunk; - // Start - threads[i].thread.start(&threads[i]); - // Wait 100ms and go to next chunk - ffw::usleep(100000); - found = true; - break; - } - } - // An empty thread was not found, wait and then try again - if(found)break; - ffw::usleep(100000); - } - } - - ffw::logger().print() << "Waiting for threads..."; - // Join all threads - for(int i = 0; i < threadsCount; i++){ - threads[i].thread.join(); - } - - // Reset indice offset - resetIndiceOffset(); - - // Generate vertices for all chunks - processedChunks = 0; - chunkBufferStruct chunkRawBuffer; - for(auto& chunk : chunkInfo){ - processedChunks++; - ffw::logger().print() << "Generating vertices: " << processedChunks << " out of: " << fileChunksTotal; - // Load a temp block to memory - loadRawBlocks(executablePath, &chunkRawBuffer, chunk.index, chunk.indexFile); - // Export block block to temp as indices and vertices - extractBlocks(executablePath, &chunkRawBuffer, chunk.index, chunk.indexFile); - } - - // End material export, we do not need it anymore - if(textureSplit && materialExport)endMaterialExport(); - - ffw::logger().print() << "Merging vertices into single OBJ..."; - - // Merge files to one OBJ file - objOutput.writeLine("# StarMade Blueprint Exporter v1.0"); - if(materialExport)objOutput.writeLine("mtllib " + fileName + ".mtl"); - objOutput.writeLine("o " + fileName); - - // Export chunks one by one - for(const auto& chunk : chunkInfo){ - ffw::file vertices; - - // Open chunk temp file which contains vertices - if(!vertices.open(executablePath + "\\temp\\" + ffw::valToString(chunk.indexFile) + "_" + ffw::valToString(chunk.index) + ".vertices.data", false, false, false)){ - ffw::logger().error() << "Error failed to open faces temp data! Index: " << chunk.index << " file: " << chunk.indexFile; - TERMINATE(false); - } - - // Read whole file and put contents in output OBJ - std::string temp; - while(!vertices.eof()){ - vertices.readLine(&temp); - if(temp.size() == 0)continue; - objOutput.writeLine(temp); - } - } - - // Export UVs - if(uvsExport && textureSplit){ - for(int i = 0; i < 4; i++){ - objOutput.writeLine("vt " + ffw::valToString(globalTextureUvs[i].x, 6) + " " + ffw::valToString(globalTextureUvs[i].y, 6)); - } - } else if(uvsExport){ - for(const auto& tile : getExtractedTiles()){ - int atlasId = tile / 256; - int posY = tile / 16; - int posX = tile - posY*16; - posY -= atlasId*16; - ffw::vec2f tilePos(posX / 16.0f, posY / 16.0f); - tilePos.y = 1.0f - tilePos.y - 0.0625f; - - for(int i = 0; i < 4; i++){ - ffw::vec2f uvs = globalTextureUvs[i]; - uvs.y = 1.0f - uvs.y; - ffw::vec2f texPos = uvs * ffw::vec2f(0.052734375f, 0.052734375f) + tilePos + 0.0048828125f; - objOutput.writeLine("vt " + ffw::valToString(texPos.x, 10) + " " + ffw::valToString(texPos.y, 10)); - } - } - } - - // Shading off - objOutput.writeLine("s off"); - - // Do the same for indices - for(const auto& chunk : chunkInfo){ - ffw::file faces; - - // Open chunk temp file which contains indices - if(!faces.open(executablePath + "\\temp\\" + ffw::valToString(chunk.indexFile) + "_" + ffw::valToString(chunk.index) + ".faces.data", false, false, false)){ - ffw::logger().error() << "Error failed to open faces temp data! Index: " << chunk.index << " file: " << chunk.indexFile; - TERMINATE(false); - } - - // Read whole file and put contents in output OBJ - std::string temp; - while(!faces.eof()){ - faces.readLine(&temp); - if(temp.size() == 0)continue; - objOutput.writeLine(temp); - } - } - - // Delete threads - delete[] threads; - - // Remove temp folder - system(std::string("rmdir /Q /S " + executablePath + "\\temp").c_str()); - - // Print some debug information - ffw::logger().print() << "OBJ file saved to: " << fileOutputFolder << "\\" << fileName << ".obj"; - ffw::logger().print() << "Export finished with no critial errors!"; - - // Terminate with success - TERMINATE(true); -} diff --git a/source/mainExporter.h b/source/mainExporter.h deleted file mode 100644 index 3aed25f..0000000 --- a/source/mainExporter.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_MAIN_EXPORTER -#define SMD2_MAIN_EXPORTER - -#include "config.h" - -// Pass NULL to argument -void* runExporter(void* DATA); - -#endif diff --git a/source/materialExport.cpp b/source/materialExport.cpp deleted file mode 100644 index 0376c84..0000000 --- a/source/materialExport.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#include "materialExport.h" -#include "config.h" - -static ffw::file material; - -///============================================================================= -bool createMaterialAtlas(){ - if(!material.open(fileOutputFolder + "\\" + fileName + ".mtl", false, true, true)){ - ffw::logger().error() << "Failed to open file for exporting material! Program might not have permissions!"; - return false; - } - material.writeLine("# StarMade Blueprint Exporter\n"); - - for(int i = 0; i < 3; i++){ - material.writeLine("newmtl Mat_Atlas_" + ffw::valToString(i)); - material.writeLine("Ka 0.000 0.000 0.000"); // Ambient color - material.writeLine("Kd 1.000 1.000 1.000"); // Diffuse color - if(useSpecularHighlight){ - material.writeLine("Ks 0.800 0.800 0.800"); // Specular level - material.writeLine("Ns 20.000"); // Glossiness - } else { - material.writeLine("Ks 0.000 0.000 0.000"); // Specular level - material.writeLine("Ns 0.000"); // Glossiness - } - material.writeLine("illum 2"); // Specular on/off (1 = disabled, 2 = enabled) - material.writeLine("Ni 1.000000"); - // Transparency (dissolved) - material.writeLine("d 1.000000"); - // Textures - if(exportDiffuse)material.writeLine("map_Kd Atlas_" + ffw::valToString(i) + "_diff." + imageExtension); - if(exportBump) material.writeLine("map_bump Atlas_" + ffw::valToString(i) + "_bump." + imageExtension); - if(exportAlpha) material.writeLine("map_d Atlas_" + ffw::valToString(i) + "_alpha." + imageExtension); - material.writeLine("\n"); - } - - material.close(); - return true; -} - -///============================================================================= -bool beginMaterialExport(){ - if(!material.open(fileOutputFolder + "\\" + fileName + ".mtl", false, true, true)){ - ffw::logger().error() << "Failed to open file for exporting material! Program might not have permissions!"; - return false; - } - material.writeLine("# StarMade Blueprint Exporter\n"); - return true; -} - -///============================================================================= -void addMaterial(const blockInfoStruct* Block, int TextureIndex){ - material.writeLine("newmtl Mat_" + Block->name + "_" + ffw::valToString(TextureIndex)); - material.writeLine("Ka 0.000 0.000 0.000"); // Ambient color - material.writeLine("Kd 1.000 1.000 1.000"); // Diffuse color - if(useSpecularHighlight){ - material.writeLine("Ks 0.800 0.800 0.800"); // Specular level - material.writeLine("Ns 20.000"); // Glossiness - } else { - material.writeLine("Ks 0.000 0.000 0.000"); // Specular level - material.writeLine("Ns 0.000"); // Glossiness - } - material.writeLine("illum 2"); // Specular on/off (1 = disabled, 2 = enabled) - if(Block->emissive) - // Emissive color - material.writeLine("ke " + ffw::valToString(Block->light.r, 6) + " " + ffw::valToString(Block->light.g, 6) - + " " + ffw::valToString(Block->light.b, 6) + " " + ffw::valToString(Block->light.a, 6)); - - material.writeLine("Ni 1.000000"); - // Transparency (dissolved) - material.writeLine("d 1.000000"); - // Textures - if(exportDiffuse)material.writeLine("map_Kd Tex_" + ffw::valToString(TextureIndex) + "_diff." + imageExtension); - if(exportBump) material.writeLine("map_bump Tex_" + ffw::valToString(TextureIndex) + "_bump." + imageExtension); - if(exportAlpha) material.writeLine("map_d Tex_" + ffw::valToString(TextureIndex) + "_alpha." + imageExtension); - material.writeLine("\n"); -} - -///============================================================================= -void endMaterialExport(){ - material.close(); -} diff --git a/source/materialExport.h b/source/materialExport.h deleted file mode 100644 index bd0366e..0000000 --- a/source/materialExport.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_MATERIAL_EXPORT -#define SMD2_EXPORTER_MATERIAL_EXPORT - -#include -#include "config.h" -#include "blockConfig.h" - -bool createMaterialAtlas(); -bool beginMaterialExport(); -void addMaterial(const blockInfoStruct* block, int TextureIndex); -void endMaterialExport(); - - -#endif - - - - diff --git a/source/polygonFiltering.h b/source/polygonFiltering.h deleted file mode 100644 index aa792a6..0000000 --- a/source/polygonFiltering.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_POLYGON_FILTERING -#define SMD2_EXPORTER_POLYGON_FILTERING - -#include -#include "config.h" - -bool removeDuplicatedFaces(chunkBufferStruct* Input, chunkBufferStruct* Output); -bool removeDuplicatedVertices(chunkBufferStruct* Input, chunkBufferStruct* Output); - -#endif - - diff --git a/source/textureExport.cpp b/source/textureExport.cpp deleted file mode 100644 index f551039..0000000 --- a/source/textureExport.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#include "textureExport.h" -#include "blockConfig.h" -#include - -static std::string textures[6] = {"t000.png", "t001.png", "t002.png", "t000_NRM.png", "t001_NRM.png", "t002_NRM.png"}; - -///============================================================================= -static void extractAlpha(unsigned char* InputPixels, int InputWidth, int InputHeight, unsigned char* OutputPixels){ - for(int y = 0; y < InputHeight; y++){ - for(int x = 0; x < InputWidth; x++){ - OutputPixels[InputWidth*y +x] = InputPixels[InputWidth*y*4 + x*4 +3]; - } - } -} - -///============================================================================= -static void subsection(unsigned char* InputPixels, int InputWidth, int InputHeight, int OutputPosX, int OutputPosY, int InputChannels, unsigned char* OutputPixels, int OutputWidth, int OutputHeight){ - for(int y = 0, p = 0; y < InputHeight; y++){ - if(y >= OutputPosY && y < OutputPosY+OutputHeight){ - memcpy(&OutputPixels[OutputWidth*p*InputChannels], &InputPixels[InputWidth*(InputHeight - y -1)*InputChannels + OutputPosX*InputChannels], OutputWidth*InputChannels); - p++; - } - } -} - -///============================================================================= -static void convertNormalToBump(unsigned char* InputPixels, int InputWidth, int InputHeight, unsigned char* OutputPixels){ - ffw::vec3f n(0.0f, 0.0f, 1.0f); - for(int y = 0; y < InputHeight; y++){ - for(int x = 0; x < InputWidth; x++){ - //OutputPixels[InputWidth*y +x] = InputPixels[InputWidth*y*3 + x*3 +0]; - ffw::vec3f norm(InputPixels[InputWidth*y*3 + x*3 +0] / 255.0f, InputPixels[InputWidth*y*3 + x*3 +1] / 255.0f, InputPixels[InputWidth*y*3 + x*3 +2] / 255.0f); - - float dot = ffw::dot(n, norm); - dot = powf(dot, 20.0f); - OutputPixels[InputWidth*y +x] = uint8_t(dot*255.0f); - } - } -} - -///============================================================================= -bool exportAtlases(const std::string& FolderPath){ - int index = 0; - - for(int i = 0; i < 6; i++){ - std::string tex = textures[i]; - unsigned char* pixels; - if(i == 3)index = 0; - int width; - int height; - ffw::imageType type; - - ffw::logger().print() << "Reading file: " << tex; - - if(!ffw::loadPNG(FolderPath + "\\" + tex, &pixels, &width, &height, &type)){ - ffw::logger().error() << "Failed to load texture: " << tex; - return 0; - } - - if(width != height){ - ffw::logger().error() << "Failed to load texture: " << tex << " Texture must have same width and height!"; - return 0; - } - - if(!(width == 1024 || width == 2048 || width == 4096)){ - ffw::logger().error() << "Failed to load texture: " << tex << " Wrong texture size! Must be either 1024, 2048, or 4096!"; - return 0; - } - - if(!(type == ffw::imageType::RGB_888 || type == ffw::imageType::RGB_ALPHA_8888)){ - ffw::logger().error() << "Failed to load texture: " << tex << " Wrong texture format! Must be either 8-bit RGB or 8-bit RGBA!"; - return 0; - } - - bool isNormal = false; - if(tex.find("_NRM") != std::string::npos){ - isNormal = true; - } - - unsigned char* outputPixelsAlpha = NULL; - unsigned char* outputNormals = NULL; - - if(!isNormal){ - outputPixelsAlpha = new unsigned char[width*height*1]; - } else { - outputNormals = new unsigned char[width*height*1]; - } - - if(isNormal){ - // Export bump map from normal map - convertNormalToBump(pixels, width, height, outputNormals); - - if(!imageSaver(fileOutputFolder + "\\atlases\\Atlas_" + ffw::valToString(index) + "_bump." + imageExtension, outputNormals, width, height, ffw::imageType::GRAYSCALE_8)){ - ffw::logger().error() << "Failed to save output texture: " << index << " Program might not have permissions or target folder " << fileOutputFolder << "\\atlases\\ does not exists!"; - return 0; - } - } else { - - // Export diffuse - if(!imageSaver(fileOutputFolder + "\\atlases\\Atlas_" + ffw::valToString(index) + "_diff." + imageExtension, pixels, width, height, type)){ - ffw::logger().error() << "Failed to save output texture: " << index << " Program might not have permissions or target folder " << fileOutputFolder << "\\atlases\\ does not exists!"; - return 0; - } - // Export alpha - if(!isNormal){ - if(type == ffw::imageType::RGB_ALPHA_8888){ - extractAlpha(pixels, width, height, outputPixelsAlpha); - - } else { - // Texture is not transparent, fill all pixels with white color - for(int p = 0; p < width*height; p++)outputPixelsAlpha[p] = 255; - } - - if(!imageSaver(fileOutputFolder + "\\atlases\\Atlas_" + ffw::valToString(index) + "_alpha." + imageExtension, outputPixelsAlpha, width, height, ffw::imageType::GRAYSCALE_8)){ - ffw::logger().error() << "Failed to save output texture: " << index << " Program might not have permissions or target folder " << fileOutputFolder << "\\atlases\\ does not exists!"; - return 0; - } - } - } - - index++; - - if(!isNormal && type == ffw::imageType::RGB_ALPHA_8888)delete[] outputPixelsAlpha; - if(isNormal)delete[] outputNormals; - } - return true; -} - -///============================================================================= -bool exportTextures(const std::string& FolderPath){ - int index = 0; - - ffw::logger().print() << "Exporting textures from: " << FolderPath; - - for(int i = 0; i < 6; i++){ - std::string tex = textures[i]; - if(i == 3)index = 0; - unsigned char* pixels; - int width; - int height; - int channels; - ffw::imageType type; - - ffw::logger().print() << "Reading file: " << tex; - - if(!ffw::loadPNG(FolderPath + "\\" + tex, &pixels, &width, &height, &type)){ - ffw::logger().error() << "Failed to load texture: " << tex; - return 0; - } - - if(width != height){ - ffw::logger().error() << "Failed to load texture: " << tex << " Texture must have same width and height!"; - return 0; - } - - if(!(width == 1024 || width == 2048 || width == 4096)){ - ffw::logger().error() << "Failed to load texture: " << tex << " Wrong texture size! Must be either 1024, 2048, or 4096!"; - return 0; - } - - if(type == ffw::imageType::RGB_888)channels = 3; - else if(type == ffw::imageType::RGB_ALPHA_8888)channels = 4; - else { - ffw::logger().error() << "Failed to load texture: " << tex << " Wrong texture format! Must be either 8-bit RGB or 8-bit RGBA!"; - return 0; - } - - int bleeding = (width / 16)*0.15625f; - - int splitSize = (width / 16) - bleeding; - - ffw::logger().print() << "Splitting texture to: " << splitSize << "x" << splitSize << " chunks!"; - - unsigned char* outputPixels = new unsigned char[splitSize*splitSize*channels]; - unsigned char* outputPixelsAlpha = NULL; - unsigned char* outputNormals = NULL; - - bool isNormal = false; - if(tex.find("_NRM") != std::string::npos){ - isNormal = true; - } - - if(!isNormal && type == ffw::imageType::RGB_ALPHA_8888){ - outputPixelsAlpha = new unsigned char[splitSize*splitSize*1]; - } - if(isNormal){ - outputNormals = new unsigned char[splitSize*splitSize*1]; - } - - for(int y = 0; y < height; y += (width / 16)){ - for(int x = 0; x < width; x += (width / 16)){ - subsection(pixels, width, height, x + bleeding/2, y + bleeding/2, channels, outputPixels, splitSize, splitSize); - - if(isNormal){ - // Export bump map from normal map - convertNormalToBump(outputPixels, splitSize, splitSize, outputNormals); - if(!imageSaver(fileOutputFolder + "\\textures\\Tex_" + ffw::valToString(index) + "_bump." + imageExtension, outputNormals, splitSize, splitSize, ffw::imageType::GRAYSCALE_8)){ - ffw::logger().error() << "Failed to save output texture: " << index << " Program might not have permissions or target folder " << fileOutputFolder << "\\textures\\ does not exists!"; - return 0; - } - } else { - - // Export diffuse - if(!imageSaver(fileOutputFolder + "\\textures\\Tex_" + ffw::valToString(index) + "_diff." + imageExtension, outputPixels, splitSize, splitSize, type)){ - ffw::logger().error() << "Failed to save output texture: " << index << " Program might not have permissions or target folder " << fileOutputFolder << "\\textures\\ does not exists!"; - return 0; - } - - // Export alpha - if(!isNormal && type == ffw::imageType::RGB_ALPHA_8888){ - extractAlpha(outputPixels, splitSize, splitSize, outputPixelsAlpha); - - if(!imageSaver(fileOutputFolder + "\\textures\\Tex_" + ffw::valToString(index) + "_alpha." + imageExtension, outputPixelsAlpha, splitSize, splitSize, ffw::imageType::GRAYSCALE_8)){ - ffw::logger().error() << "Failed to save output texture: " << index << " Program might not have permissions or target folder " << fileOutputFolder << "\\textures\\ does not exists!"; - return 0; - } - } - } - - index++; - } - } - - if(!isNormal && type == ffw::imageType::RGB_ALPHA_8888)delete[] outputPixelsAlpha; - if(isNormal)delete[] outputNormals; - delete[] outputPixels; - } - return true; -} diff --git a/source/textureExport.h b/source/textureExport.h deleted file mode 100644 index 32e424f..0000000 --- a/source/textureExport.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - Distributed under the MIT License. - Copyright (C) 2015 by Matus Novak matusnov@gmail.com -*/ - -#ifndef SMD2_EXPORTER_TEXTURE_EXPORT -#define SMD2_EXPORTER_TEXTURE_EXPORT - -#include -#include "config.h" - -bool exportTextures(const std::string& FolderPath); -bool exportAtlases(const std::string& FolderPath); - -#endif - - - diff --git a/source/widgets.cpp b/source/widgets.cpp new file mode 100644 index 0000000..b789deb --- /dev/null +++ b/source/widgets.cpp @@ -0,0 +1,126 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include "widgets.h" +#include "CommCtrl.h" + +HWND sm2obj::createButton(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID){ + return CreateWindowEx(0, "Button", Label.c_str(), WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_PUSHBUTTON, PosX, PosY, Width, Height, Handle, (HMENU)ID, NULL, NULL); +} + +HWND sm2obj::createCheckbox(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID){ + return CreateWindowEx(0, "Button", Label.c_str(), BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, PosX, PosY, Width, Height, Handle, (HMENU)ID, NULL, NULL); +} + +HWND sm2obj::createRadio(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID){ + return CreateWindowEx(0, "Button", Label.c_str(), WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_RADIOBUTTON, PosX, PosY, Width, Height, Handle, (HMENU)ID, NULL, NULL); +} + +HWND sm2obj::createTextInput(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID){ + return CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", Label.c_str(), WS_CHILD | WS_VISIBLE | ES_AUTOVSCROLL | ES_AUTOHSCROLL, PosX, PosY, Width, Height, Handle, (HMENU)ID, NULL, NULL); +} + +HWND sm2obj::createCombo(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID){ + return CreateWindowEx(WS_EX_STATICEDGE, "COMBOBOX", Label.c_str(), WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_DROPDOWNLIST, PosX, PosY, Width, Height, Handle, (HMENU)ID, NULL, NULL); +} + +HWND sm2obj::createList(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID){ + return CreateWindowEx(0, "LISTBOX", Label.c_str(), WS_BORDER | WS_CHILD | WS_VISIBLE, PosX, PosY, Width, Height, Handle, (HMENU)ID, NULL, NULL); +} + +HWND sm2obj::createProgressbar(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID){ + return CreateWindowEx(0, PROGRESS_CLASS, NULL, WS_CHILD | WS_VISIBLE, PosX, PosY, Width, Height, Handle, (HMENU)ID, NULL, NULL); +} + +HWND sm2obj::createLabel(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID){ + return CreateWindowEx(WS_EX_TRANSPARENT, "STATIC", Label.c_str(), WS_CHILD | WS_VISIBLE | SS_LEFT | WS_SYSMENU , PosX, PosY, Width, Height, Handle, (HMENU)ID, NULL, NULL); +} + +HWND sm2obj::createGroup(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID){ + return CreateWindowEx(0, "BUTTON", Label.c_str(), WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_GROUPBOX, PosX, PosY, Width, Height, Handle, (HMENU)ID, NULL, NULL); +} + +bool sm2obj::getCheckboxValue(HWND& Checkbox){ + return SendMessage(Checkbox, BM_GETCHECK, 0, 0); +} + +bool sm2obj::getRadioValue(HWND& Radio){ + return SendMessage(Radio, BM_GETCHECK, 0, 0); +} + +std::string sm2obj::getTextInputValue(HWND& TextInput){ + int len = SendMessage(TextInput, WM_GETTEXTLENGTH, 0, 0); + std::string buffer; + buffer.resize(len); + SendMessage(TextInput, WM_GETTEXT, (WPARAM)len+1, (LPARAM)&buffer[0]); + return buffer; +} + +int sm2obj::getComboValue(HWND& Combo){ + return SendMessage(Combo, CB_GETCURSEL , 0, 0); +} + +int sm2obj::getListValue(HWND& List){ + return SendMessage(List, LB_GETCURSEL, 0, 0); +} + +void sm2obj::setCheckboxValue(HWND& Checkbox, bool Value){ + if(Value)SendMessage(Checkbox, BM_SETCHECK, BST_CHECKED, 0); + else SendMessage(Checkbox, BM_SETCHECK, BST_UNCHECKED, 0); +} + +void sm2obj::setRadioValue(HWND& Radio, bool Value){ + if(Value)SendMessage(Radio, BM_SETCHECK, BST_CHECKED, 0); + else SendMessage(Radio, BM_SETCHECK, BST_UNCHECKED, 0); +} + +void sm2obj::setTextInputValue(HWND& TextInput, const std::string& Value){ + SendMessage(TextInput, WM_SETTEXT, 0, (LPARAM)Value.c_str()); +} + +void sm2obj::setComboValue(HWND& Combo, int Value){ + SendMessage(Combo, CB_SETCURSEL, Value, 0); +} + +void sm2obj::addComboValue(HWND& Combo, const std::string& Value){ + SendMessage(Combo, CB_ADDSTRING, 0, (LPARAM)Value.c_str()); +} + +void sm2obj::setListValue(HWND& List, int Value){ + SendMessage(List, LB_SETCURSEL, Value, 0); +} + +void sm2obj::addListValue(HWND& List, const std::string& Value){ + SendMessage(List, LB_ADDSTRING, 0, (LPARAM)Value.c_str()); +} + +void sm2obj::setProgressbarValue(HWND& Progressbar, int Value){ + SendMessage(Progressbar, PBM_SETPOS, Value, 0); +} + +void sm2obj::setLabelValue(HWND& Label, const std::string& Value){ + SendMessage(Label, WM_SETTEXT, 0, (LPARAM)Value.c_str()); +} + +void sm2obj::setWidgetFont(HWND& Widget, HFONT& Font){ + SendMessage(Widget, WM_SETFONT, (WPARAM)Font, TRUE); +} + +HFONT sm2obj::createFont(const std::string& Name, int Size, int Weight){ + return CreateFont(Size,0,0,0,Weight,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_DEVICE_PRECIS,CLIP_MASK,ANTIALIASED_QUALITY, DEFAULT_PITCH,Name.c_str()); +} + +void sm2obj::showModalInfo(HWND Handle, const std::string& Title, const std::string& Message){ + MessageBox(Handle, Message.c_str(), Title.c_str(), MB_OK | MB_ICONINFORMATION); +} + +void sm2obj::showModalError(HWND Handle, const std::string& Title, const std::string& Message){ + MessageBox(Handle, Message.c_str(), Title.c_str(), MB_OK | MB_ICONERROR); +} + +void sm2obj::showModalWarning(HWND Handle, const std::string& Title, const std::string& Message){ + MessageBox(Handle, Message.c_str(), Title.c_str(), MB_OK | MB_ICONWARNING); +} diff --git a/source/widgets.h b/source/widgets.h new file mode 100644 index 0000000..89c2bfb --- /dev/null +++ b/source/widgets.h @@ -0,0 +1,50 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_WIDGETS +#define SM2OBJ_WIDGETS + +#include +#include +#include + +namespace sm2obj { + HWND createButton(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID); + HWND createCheckbox(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID); + HWND createRadio(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID); + HWND createTextInput(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID); + HWND createCombo(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID); + HWND createList(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID); + HWND createProgressbar(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID); + HWND createLabel(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID); + HWND createGroup(HWND Handle, const std::string& Label, int PosX, int PosY, int Width, int Height, int ID); + + bool getCheckboxValue(HWND& Checkbox); + bool getRadioValue(HWND& Radio); + std::string getTextInputValue(HWND& TextInput); + int getComboValue(HWND& Combo); + int getListValue(HWND& List); + + void setCheckboxValue(HWND& Checkbox, bool Value); + void setRadioValue(HWND& Radio, bool Value); + void setTextInputValue(HWND& TextInput, const std::string& Value); + void setComboValue(HWND& Combo, int Value); + void addComboValue(HWND& Combo, const std::string& Value); + void setListValue(HWND& List, int Value); + void addListValue(HWND& List, const std::string& Value); + void setProgressbarValue(HWND& Progressbar, int Value); + void setLabelValue(HWND& Label, const std::string& Value); + + void setWidgetFont(HWND& Widget, HFONT& Font); + + HFONT createFont(const std::string& Name, int Size, int Weight); + + void showModalInfo(HWND Handle, const std::string& Title, const std::string& Message); + void showModalError(HWND Handle, const std::string& Title, const std::string& Message); + void showModalWarning(HWND Handle, const std::string& Title, const std::string& Message); +}; + +#endif diff --git a/source/window.cpp b/source/window.cpp new file mode 100644 index 0000000..9fe49e4 --- /dev/null +++ b/source/window.cpp @@ -0,0 +1,632 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#include "window.h" +#include "CommCtrl.h" +#include "widgets.h" +#include +#include +#include "exporter/textureExport.h" +#include "exporter/exportBlueprint.h" +#include + +static int id = 100; + +static int getNewId(){ + id++; + return id; +} + +sm2obj::defaults userInput; +sm2obj::window* sm2obj::window::instance; +HWND* sm2obj::window::hwndPtr; +HWND sm2obj::window::hwnd; +ffw::thread sm2obj::window::exportThread; +ffw::mutex sm2obj::window::exportThreadMutex; +bool sm2obj::window::warningLog; + +sm2obj::window::window(){ + instance = this; + + buttonSelectInputFolderID = getNewId(); + buttonSelectInputDataID = getNewId(); + buttonSelectOutputFolderID = getNewId(); + buttonSelectTextureFolderID = getNewId(); + textInputInputFolderID = getNewId(); + textInputInputDataID = getNewId(); + textInputOutputFolderID = getNewId(); + textInputTextureFolderID = getNewId(); + comboUvMapSettingsID = getNewId(); + checkboxExportMaterialsID = getNewId(); + checkboxExportDiffuseID = getNewId(); + checkboxExportAlphaID = getNewId(); + checkboxExportNormalID = getNewId(); + checkboxExportEmissiveID = getNewId(); + comboTextureNormalsID = getNewId(); + textInputNumOfThreadsID = getNewId(); + comboTextureFormatID = getNewId(); + comboTextureTypeID = getNewId(); + buttonExportTexturesID = getNewId(); + comboTextureSizeID = getNewId(); + progressbarID = getNewId(); + labelProgressTextID = getNewId(); + buttonExportObjectID = getNewId(); + checkboxSpecularHighlightID = getNewId(); + checkboxExportAttachmentsID = getNewId(); +} + +sm2obj::window::~window(){ +} + +int sm2obj::window::create(HINSTANCE hThisInstance, int nCmdShow){ + config::exportLogErrorFunc = &callbackExportLogError; + config::exportLogDebugFunc = &callbackExportLogDebug; + config::exportLogInfoFunc = &callbackExportLogInfo; + config::exportLogWarningFunc = &callbackExportLogWarning; + config::exportExitFunc = &callbackExportExit; + config::exportProgressFunc = &callbackExportProgress; + + ZeroMemory(&wincl, sizeof(wincl)); + + wincl.style = 0; + wincl.lpszMenuName = NULL; + wincl.lpszClassName = "WindowClass"; + wincl.lpfnWndProc = this->WndProc; + wincl.hInstance = hThisInstance; + wincl.hIconSm = LoadIcon(NULL, IDC_ICON); + wincl.hIcon = LoadIcon(NULL, IDC_ICON); + wincl.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wincl.cbWndExtra = 0; + wincl.cbSize = sizeof(WNDCLASSEX); + wincl.cbClsExtra = 0; + + if(!RegisterClassEx(&wincl)){ + MessageBox(NULL, "Could not register window!", "Error", MB_OK | MB_ICONERROR); + return -1; + } + + hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "WindowClass", "Window", WS_MINIMIZEBOX | WS_SYSMENU | WS_CAPTION , CW_USEDEFAULT, CW_USEDEFAULT, 500, 660, NULL, NULL, hThisInstance, NULL); + + if (hwnd == NULL){ + MessageBox(NULL, "Could not create window!", "Error", MB_OK | MB_ICONERROR); + return -1; + } + + ShowWindow(hwnd, nCmdShow); + UpdateWindow(hwnd); + + while (GetMessage(&msg, 0, 0, 0)){ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return msg.wParam; + +} + +void sm2obj::window::destroy(){ + userInput.inputBlueprintFolder = getTextInputValue(textInputInputFolder); + userInput.inputDataFolder = getTextInputValue(textInputInputData); + userInput.outputFileFolder = getTextInputValue(textInputOutputFolder); + userInput.outputTextureFolder = getTextInputValue(textInputTextureFolder); + userInput.uvMapsOptions = getComboValue(comboUvMapSettings); + userInput.exportMaterials = getCheckboxValue(checkboxExportMaterials); + userInput.useDiffuseTextures = getCheckboxValue(checkboxExportDiffuse); + userInput.useAlphaTextures = getCheckboxValue(checkboxExportAlpha); + userInput.useNormalTextures = getCheckboxValue(checkboxExportNormal); + userInput.useEmissiveTextures = getCheckboxValue(checkboxExportEmissive); + userInput.specularHighlight = getCheckboxValue(checkboxSpecularHighlight); + userInput.numOfThreads = getTextInputValue(textInputNumOfThreads); + userInput.textureOutputFormat = getComboValue(comboTextureFormat); + userInput.textureNormals = getComboValue(comboTextureNormals); + userInput.textureExportType = getComboValue(comboTextureType); + userInput.textureTileSize = getComboValue(comboTextureSize); + userInput.exportAttachments = getCheckboxValue(checkboxExportAttachments); + userInput.save(ffw::getExecutablePath() + "\\user-input.json"); +} + +void sm2obj::window::callbackExportExit(bool Success){ + if(Success){ + if(warningLog){ + showModalWarning(hwnd, "Warning!", "Blueprint exported! Process ended with warning(s)! Look for warning messages in log.txt for more information."); + } else { + showModalInfo(hwnd, "Success!", "Blueprint exported! Process ended with no errors."); + } + } +} + +void sm2obj::window::callbackExportProgress(int Progress, int Total){ + int p = (Progress / float(Total)) * 100; + setProgressbarValue(instance->progressbar, p); +} + +void sm2obj::window::callbackExportLogDebug(const std::string& Message){ + ffw::logger().debug() << Message; +} + +void sm2obj::window::callbackExportLogError(const std::string& Message){ + ffw::logger().error() << Message; + showModalError(hwnd, "Error!", Message); + +} + +void sm2obj::window::callbackExportLogWarning(const std::string& Message){ + ffw::logger().warning() << Message; + warningLog = true; +} + +void sm2obj::window::callbackExportLogInfo(const std::string& Message){ + ffw::logger().print() << Message; + setLabelValue(instance->labelProgressText, Message); +} + +void* sm2obj::window::exportObjectFunc(void* Data){ + warningLog = false; + callbackExportLogDebug("exportObjectFunc start"); + exportThreadMutex.lock(); + + do{ + int textureFormat = getComboValue(instance->comboTextureFormat); + if(textureFormat == 0){ + config::imageExtension = "png"; + } else if(textureFormat == 1){ + config::imageExtension = "bmp"; + } else if(textureFormat == 2){ + config::imageExtension = "tga"; + } else if(textureFormat == 3){ + config::imageExtension = "tiff"; + } else { + callbackExportLogError("Please, select texture format!"); + break; + } + + std::string blueprintFolder = getTextInputValue(instance->textInputInputFolder); + if(blueprintFolder.size() == 0){ + callbackExportLogError("Please, select blueprint folder!"); + break; + } + + std::string inputDataFolder = getTextInputValue(instance->textInputInputData); + if(inputDataFolder.size() == 0){ + callbackExportLogError("Please, select StarMade data folder!"); + break; + } + inputDataFolder += "\\config"; + + std::string outputFolder = getTextInputValue(instance->textInputOutputFolder); + if(outputFolder.size() == 0){ + callbackExportLogError("Please, select output folder!"); + break; + } + + if(blueprintFolder[blueprintFolder.size()] == '\\')blueprintFolder.pop_back(); + + std::string outputName; + size_t pos = blueprintFolder.find_last_of('\\'); + if(pos != std::string::npos && pos != blueprintFolder.size()-1){ + outputName = blueprintFolder.substr(pos+1, blueprintFolder.size()-pos-1); + callbackExportLogDebug("Output name: " + outputName); + } else { + callbackExportLogError("Could not detect blueprint folder name! Make sure the path is correct!"); + break; + } + + int numOfThreads; + try { + numOfThreads = ffw::stringToVal(getTextInputValue(instance->textInputNumOfThreads)); + } catch (std::exception& e){ + callbackExportLogError("Wrong number of threads! Please, write a number"); + break; + } + + if(numOfThreads <= 0 || numOfThreads > 16){ + callbackExportLogError("Wrong number of threads! Use values between 1 and 16"); + break; + } + + bool exportMaterials = getCheckboxValue(instance->checkboxExportMaterials); + bool exportDiffuse = getCheckboxValue(instance->checkboxExportDiffuse); + bool exportAlpha = getCheckboxValue(instance->checkboxExportAlpha); + bool exportNormal = getCheckboxValue(instance->checkboxExportNormal); + bool exportEmissive = getCheckboxValue(instance->checkboxExportEmissive); + bool specularHighloght = getCheckboxValue(instance->checkboxSpecularHighlight); + bool attachments = getCheckboxValue(instance->checkboxExportAttachments); + bool exportUvs; + bool useAtlas; + int uvOption = getComboValue(instance->comboUvMapSettings); + + if(uvOption == 0){ + useAtlas = false; + exportUvs = false; + } else if(uvOption == 1){ + exportUvs = true; + useAtlas = false; + } else if(uvOption == 2){ + exportUvs = true; + useAtlas = true; + } else { + callbackExportLogError("Please, select what to do with UV maps!"); + break; + } + + callbackExportLogDebug("Input config folder: " + inputDataFolder); + callbackExportLogDebug("Input blueprint folder: " + blueprintFolder); + callbackExportLogDebug("Output folder: " + outputFolder); + callbackExportLogDebug("Output file name: " + outputName); + callbackExportLogDebug("Export UVs: " + ffw::valToString(exportUvs)); + callbackExportLogDebug("Use atlas: " + ffw::valToString(useAtlas)); + callbackExportLogDebug("Use diffuse textures: " + ffw::valToString(exportDiffuse)); + callbackExportLogDebug("Use alpha textures: " + ffw::valToString(exportAlpha)); + callbackExportLogDebug("Use normal textures: " + ffw::valToString(exportNormal)); + callbackExportLogDebug("Use emissive textures: " + ffw::valToString(exportEmissive)); + callbackExportLogDebug("Use specular highlight: " + ffw::valToString(specularHighloght)); + callbackExportLogDebug("number of threads: " + ffw::valToString(numOfThreads)); + callbackExportLogDebug("Export attachments: " + ffw::valToString(attachments)); + + exportBlueprint(inputDataFolder, blueprintFolder, outputFolder, outputName, + exportUvs, useAtlas, exportMaterials, + exportDiffuse, exportAlpha, exportNormal, exportEmissive, + specularHighloght, numOfThreads, attachments); + + // Remove temp folder + system(std::string("rmdir /Q /S " + ffw::getExecutablePath() + "\\temp").c_str()); + + setProgressbarValue(instance->progressbar, 0); + setLabelValue(instance->labelProgressText, "Idle"); + }while(0); + + exportThreadMutex.unlock(); + callbackExportLogDebug("exportObjectFunc end"); + return NULL; +} + +void* sm2obj::window::exportTextuesFunc(void* Data){ + warningLog = false; + callbackExportLogDebug("exportTextuesFunc started"); + exportThreadMutex.lock(); + + do{ + int textureFormat = getComboValue(instance->comboTextureFormat); + if(textureFormat == 0){ + config::imageSaverFunc = &ffw::savePNG; + config::imageExtension = "png"; + } else if(textureFormat == 1){ + config::imageSaverFunc = &ffw::saveBMP; + config::imageExtension = "bmp"; + } else if(textureFormat == 2){ + config::imageSaverFunc = &ffw::saveTGA; + config::imageExtension = "tga"; + } else if(textureFormat == 3){ + config::imageSaverFunc = &ffw::saveTIFF; + config::imageExtension = "tiff"; + } else { + callbackExportLogError("Please, select texture format!"); + break; + } + + std::string inputFolder = getTextInputValue(instance->textInputInputData); + if(inputFolder.size() == 0){ + callbackExportLogError("Please, select StarMade data folder!"); + break; + } + + std::string inputDataFolder = getTextInputValue(instance->textInputInputData); + if(inputDataFolder.size() == 0){ + callbackExportLogError("Please, select StarMade data folder!"); + break; + } + + std::string outputFolder = getTextInputValue(instance->textInputTextureFolder); + if(outputFolder.size() == 0){ + callbackExportLogError("Please, select texture output folder!"); + break; + } + + int texture = getComboValue(instance->comboTextureSize); + int textureSize; + if(texture == 0){ + inputFolder += "\\textures\\block\\Default\\64"; + textureSize = 64; + } else if(texture == 1){ + inputFolder += "\\textures\\block\\Default\\128"; + textureSize = 128; + } else if(texture == 2){ + inputFolder += "\\textures\\block\\Default\\256"; + textureSize = 256; + } else { + callbackExportLogError("Please, select texture size!"); + break; + } + + bool normals; + int convertNormals = getComboValue(instance->comboTextureNormals); + if(convertNormals == 0){ + normals = false; + } else if(convertNormals == 1){ + normals = true; + } else { + callbackExportLogError("Please, select what to do with normals!"); + break; + } + + int textureType = getComboValue(instance->comboTextureType); + if(textureType == 0){ + //callbackExportLogDebug("Selected \"Split tiles\" option"); + } else if(textureType == 1){ + //callbackExportLogDebug("Selected \"Use atlas\" option"); + } else { + callbackExportLogError("Please, select export type of textures! (Separate tiles or use whole atlas)"); + break; + } + + callbackExportLogDebug("StarMade texture folder: " + inputFolder); + callbackExportLogDebug("Texture output folder: " + outputFolder); + callbackExportLogDebug("Convert normals: " + ffw::valToString(normals)); + callbackExportLogDebug("Use atlas instead of splitting tiles: " + ffw::valToString(textureType)); + + if(textureType == 0){ + exportTextures(inputFolder, outputFolder, normals); + } else { + exportAtlases(inputFolder, outputFolder, normals); + exportEmissiveAtlas(inputDataFolder + "\\config", outputFolder, textureSize); + } + + setProgressbarValue(instance->progressbar, 0); + setLabelValue(instance->labelProgressText, "Idle"); + } while(0); + + exportThreadMutex.unlock(); + callbackExportLogDebug("exportTextuesFunc ended"); + return NULL; +} + +void sm2obj::window::widgetCallback(HWND hwnd, WPARAM wParam){ + if(wParam == buttonSelectInputFolderID){ + std::string currentPath = getTextInputValue(textInputInputFolder); + std::wstring folder = ffw::openFolderDialog(L"Select blueprint folder", ffw::ansiToWstr(currentPath)); + if(folder.size() != 0){ + setTextInputValue(textInputInputFolder, ffw::wstrToAnsi(folder)); + } + } + + if(wParam == buttonSelectInputDataID){ + std::string currentPath = getTextInputValue(textInputInputData); + std::wstring folder = ffw::openFolderDialog(L"Select StarMade data folder", ffw::ansiToWstr(currentPath)); + if(folder.size() != 0){ + setTextInputValue(textInputInputData, ffw::wstrToAnsi(folder)); + } + } + + if(wParam == buttonSelectOutputFolderID){ + std::string currentPath = getTextInputValue(textInputOutputFolder); + std::wstring folder = ffw::openFolderDialog(L"Select output folder for OBJ and MTL file", ffw::ansiToWstr(currentPath)); + if(folder.size() != 0){ + setTextInputValue(textInputOutputFolder, ffw::wstrToAnsi(folder)); + } + } + + if(wParam == buttonSelectTextureFolderID){ + std::string currentPath = getTextInputValue(textInputTextureFolder); + std::wstring folder = ffw::openFolderDialog(L"Select output folder for textures", ffw::ansiToWstr(currentPath)); + if(folder.size() != 0){ + setTextInputValue(textInputTextureFolder, ffw::wstrToAnsi(folder)); + } + } + + if(wParam == buttonExportObjectID){ + if(exportThreadMutex.tryLock()){ + exportThreadMutex.unlock(); + exportThread.bindFunction(&sm2obj::window::exportObjectFunc); + exportThread.start(NULL); + + } else { + showModalError(hwnd, "Error!", "Process has already begun"); + } + } + + if(wParam == buttonExportTexturesID){ + if(exportThreadMutex.tryLock()){ + exportThreadMutex.unlock(); + exportThread.bindFunction(&sm2obj::window::exportTextuesFunc); + exportThread.start(NULL); + } else { + showModalError(hwnd, "Error!", "Process has already begun"); + } + } +} + +void sm2obj::window::createWidgets(HWND hwnd){ + HFONT hFont = createFont("Segoe UI", 19, 550); + + int posY = 5; + int posYoff = 5; + HWND label0 = createLabel(hwnd, "Select blueprint folder:", 5, posY, 480, 20, 0); + posY += 20; + textInputInputFolder = createTextInput(hwnd, "", 5, posY, 430, 25, textInputInputFolderID); + buttonSelectInputFolder = createButton(hwnd, "...", 440, posY, 45, 25, buttonSelectInputFolderID); + posY += 30; + + HWND label1 = createLabel(hwnd, "Select StarMade data folder:", 5, posY, 480, 20, 0); + posY += 20; + textInputInputData = createTextInput(hwnd, "", 5, posY, 430, 25, textInputInputDataID); + buttonSelectInputData = createButton(hwnd, "...", 440, posY, 45, 25, buttonSelectInputDataID); + posY += 30; + + HWND label2 = createLabel(hwnd, "Select output folder for OBJ and MTL files:", 5, posY, 480, 20, 0); + posY += 20; + textInputOutputFolder = createTextInput(hwnd, "", 5, posY, 430, 25, textInputOutputFolderID); + buttonSelectOutputFolder = createButton(hwnd, "...", 440, posY, 45, 25, buttonSelectOutputFolderID); + posY += 30; + + HWND group0 = createGroup(hwnd, "UV maps options:", 5, posY, 480, 55, 0); + posY += 20; + comboUvMapSettings = createCombo(hwnd, "", 10, posY, 470, 25, comboUvMapSettingsID); + addComboValue(comboUvMapSettings, "Do not export UV maps"); + addComboValue(comboUvMapSettings, "Export UV maps and split tiles (separate material per tile)"); + addComboValue(comboUvMapSettings, "Export UV maps and use atlas (single material per atlas)"); + + posY += 40; + posYoff = posY; + HWND group1 = createGroup(hwnd, "Material options:", 5, posY, 235, 125, 0); + posY += 20; + checkboxExportMaterials = createCheckbox(hwnd, "Export materials", 10, posY, 110, 20, checkboxExportMaterialsID); + posY += 30; + checkboxExportDiffuse = createCheckbox(hwnd, "Diffuse", 10, posY, 110, 20, checkboxExportDiffuseID); + checkboxExportAlpha = createCheckbox(hwnd, "Alpha", 10, posY +20, 110, 20, checkboxExportAlphaID); + checkboxExportNormal = createCheckbox(hwnd, "Normal / bump", 10 + 110, posY, 110, 20, checkboxExportNormalID); + checkboxExportEmissive = createCheckbox(hwnd, "Emissive", 10 + 110, posY +20, 110, 20, checkboxExportEmissiveID); + posY += 50; + checkboxSpecularHighlight = createCheckbox(hwnd, "Specular highlight", 10, posY, 200, 20, checkboxSpecularHighlightID); + + posY = posYoff; + HWND group2 = createGroup(hwnd, "System options:", 250, posY, 235, 55, 0); + posY += 20; + HWND label3 = createLabel(hwnd, "Number of threads (max 16):", 255, posY, 190, 20, 0); + textInputNumOfThreads = createTextInput(hwnd, "", 255+195, posY, 30, 25, textInputNumOfThreadsID); + + posY += 50; + HWND group3 = createGroup(hwnd, "Attachments options:", 250, posY, 235, 55, 0); + posY += 20; + checkboxExportAttachments = createCheckbox(hwnd, "Export attached turrets/ships", 255, posY, 225, 25, checkboxExportAttachmentsID); + + posY += 40; + buttonExportObject = createButton(hwnd, "Export object", 170, posY, 140, 35, buttonExportObjectID); + + posY += 40; + HWND group4 = createGroup(hwnd, "Texture export options:", 5, posY, 480, 170, 0); + posY += 20; + HWND label4 = createLabel(hwnd, "Select output folder for textures:", 10, posY, 470, 20, 0); + posY += 20; + textInputTextureFolder = createTextInput(hwnd, "", 10, posY, 420, 25, textInputTextureFolderID); + buttonSelectTextureFolder = createButton(hwnd, "...", 435, posY, 45, 25, buttonSelectTextureFolderID); + posY += 30; + posYoff = posY; + HWND label5 = createLabel(hwnd, "Output format:", 10, posY, 100, 20, 0); + comboTextureFormat = createCombo(hwnd, "", 110, posY, 125, 25, comboTextureFormatID); + addComboValue(comboTextureFormat, "*.PNG"); + addComboValue(comboTextureFormat, "*.BMP"); + addComboValue(comboTextureFormat, "*.TGA"); + addComboValue(comboTextureFormat, "*.TIFF"); + posY += 30; + HWND label6 = createLabel(hwnd, "Export as:", 10, posY, 100, 20, 0); + comboTextureType = createCombo(hwnd, "", 110, posY, 125, 25, comboTextureTypeID); + addComboValue(comboTextureType, "Separate tiles"); + addComboValue(comboTextureType, "Whole atlas"); + + posY = posYoff; + HWND label7 = createLabel(hwnd, "Tile size:", 245, posY, 100, 20, 0); + comboTextureSize = createCombo(hwnd, "", 350, posY, 130, 25, comboTextureSizeID); + addComboValue(comboTextureSize, "64px"); + addComboValue(comboTextureSize, "128px"); + addComboValue(comboTextureSize, "256px"); + posY += 30; + HWND label8 = createLabel(hwnd, "Normals:", 245, posY, 100, 20, 0); + comboTextureNormals = createCombo(hwnd, "", 350, posY, 130, 25, comboTextureNormalsID); + addComboValue(comboTextureNormals, "Use original"); + addComboValue(comboTextureNormals, "Convert to bump"); + posY += 30; + buttonExportTextures = createButton(hwnd, "Export textures", 170, posY, 140, 35, buttonExportTexturesID); + + posY += 55; + progressbar = createProgressbar(hwnd, "", 5, posY, 480, 25, progressbarID); + posY += 30; + HWND label9 = createLabel(hwnd, "Status:", 5, posY, 45, 20, 0); + labelProgressText = createLabel(hwnd, "Idle", 55, posY, 380, 20, labelProgressTextID); + + setWidgetFont(label0, hFont); + setWidgetFont(label1, hFont); + setWidgetFont(label2, hFont); + setWidgetFont(label3, hFont); + setWidgetFont(label4, hFont); + setWidgetFont(label5, hFont); + setWidgetFont(label6, hFont); + setWidgetFont(label7, hFont); + setWidgetFont(label8, hFont); + setWidgetFont(label9, hFont); + setWidgetFont(labelProgressText, hFont); + setWidgetFont(textInputInputFolder, hFont); + setWidgetFont(textInputInputData, hFont); + setWidgetFont(textInputOutputFolder, hFont); + setWidgetFont(textInputTextureFolder, hFont); + setWidgetFont(buttonSelectInputFolder, hFont); + setWidgetFont(buttonSelectInputData, hFont); + setWidgetFont(buttonSelectOutputFolder, hFont); + setWidgetFont(buttonSelectTextureFolder, hFont); + setWidgetFont(comboUvMapSettings, hFont); + setWidgetFont(checkboxExportMaterials, hFont); + setWidgetFont(checkboxExportDiffuse, hFont); + setWidgetFont(checkboxExportAlpha, hFont); + setWidgetFont(checkboxExportNormal, hFont); + setWidgetFont(checkboxExportEmissive, hFont); + setWidgetFont(checkboxSpecularHighlight, hFont); + setWidgetFont(checkboxExportAttachments, hFont); + setWidgetFont(comboTextureFormat, hFont); + setWidgetFont(comboTextureType, hFont); + setWidgetFont(comboTextureSize, hFont); + setWidgetFont(comboTextureNormals, hFont); + setWidgetFont(buttonExportTextures, hFont); + setWidgetFont(buttonExportObject, hFont); + setWidgetFont(textInputNumOfThreads, hFont); + setWidgetFont(group0, hFont); + setWidgetFont(group1, hFont); + setWidgetFont(group2, hFont); + setWidgetFont(group3, hFont); + setWidgetFont(group4, hFont); + + if(userInput.load(ffw::getExecutablePath() + "\\user-input.json")){ + setTextInputValue (textInputInputFolder, userInput.inputBlueprintFolder); + setTextInputValue (textInputInputData, userInput.inputDataFolder); + setTextInputValue (textInputOutputFolder, userInput.outputFileFolder); + setTextInputValue (textInputTextureFolder, userInput.outputTextureFolder); + setComboValue (comboUvMapSettings, userInput.uvMapsOptions); + setCheckboxValue (checkboxExportMaterials, userInput.exportMaterials); + setCheckboxValue (checkboxExportDiffuse, userInput.useDiffuseTextures); + setCheckboxValue (checkboxExportAlpha, userInput.useAlphaTextures); + setCheckboxValue (checkboxExportNormal, userInput.useNormalTextures); + setCheckboxValue (checkboxExportEmissive, userInput.useEmissiveTextures); + setCheckboxValue (checkboxSpecularHighlight, userInput.specularHighlight); + setTextInputValue (textInputNumOfThreads, userInput.numOfThreads); + setComboValue (comboTextureFormat, userInput.textureOutputFormat); + setComboValue (comboTextureNormals, userInput.textureNormals); + setComboValue (comboTextureType, userInput.textureExportType); + setComboValue (comboTextureSize, userInput.textureTileSize); + setCheckboxValue (checkboxExportAttachments, userInput.exportAttachments); + } +} + +LRESULT CALLBACK sm2obj::window::WndProc(HWND hwnd, UINT Msg, WPARAM wParam , LPARAM lParam){ + switch (Msg){ + + case WM_CREATE:{ + instance->createWidgets(hwnd); + break; + } + case WM_CTLCOLORSTATIC:{ + if(GetDlgItem(hwnd, 3) == (HWND)lParam){ + SetBkMode( (HDC)wParam, TRANSPARENT); + SetTextColor((HDC)wParam, RGB(255,0,0)); + return (BOOL) GetStockObject(HOLLOW_BRUSH); + } + } + case WM_COMMAND:{ + instance->widgetCallback(hwnd, wParam); + break; + } + case WM_CLOSE:{ + instance->destroy(); + DestroyWindow(hwnd); + break; + } + case WM_DESTROY:{ + PostQuitMessage(0); + break; + } + default:{ + return DefWindowProc(hwnd, Msg, wParam, lParam); + } + } + return 0; +} diff --git a/source/window.h b/source/window.h new file mode 100644 index 0000000..b664f6f --- /dev/null +++ b/source/window.h @@ -0,0 +1,101 @@ +/* +* This file is part of SM2OBJ project. +* Copyright (C) 2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef SM2OBJ_WINDOW +#define SM2OBJ_WINDOW + +#include +#include +#include "defaults.h" + +namespace sm2obj { + class window { + public: + window(); + ~window(); + + int create(HINSTANCE hThisInstance, int nCmdShow); + + private: + void destroy(); + void createWidgets(HWND hwnd); + void widgetCallback(HWND hwnd, WPARAM wParam); + static window* instance; + static HWND* hwndPtr; + static LRESULT CALLBACK WndProc(HWND hwnd, UINT Msg, WPARAM wParam , LPARAM lParam); + + static ffw::thread exportThread; + static ffw::mutex exportThreadMutex; + static void* exportObjectFunc(void* Data); + static void* exportTextuesFunc(void* Data); + + static void callbackExportExit(bool Success); + static void callbackExportProgress(int Progress, int Total); + static void callbackExportLogDebug(const std::string& Message); + static void callbackExportLogError(const std::string& Message); + static void callbackExportLogWarning(const std::string& Message); + static void callbackExportLogInfo(const std::string& Message); + + WNDCLASSEX wincl; + MSG msg; + static HWND hwnd; + static bool warningLog; + + WPARAM buttonSelectInputFolderID; + WPARAM buttonSelectInputDataID; + WPARAM buttonSelectOutputFolderID; + WPARAM buttonSelectTextureFolderID; + WPARAM textInputInputFolderID; + WPARAM textInputInputDataID; + WPARAM textInputOutputFolderID; + WPARAM textInputTextureFolderID; + WPARAM comboUvMapSettingsID; + WPARAM checkboxExportMaterialsID; + WPARAM checkboxExportDiffuseID; + WPARAM checkboxExportAlphaID; + WPARAM checkboxExportNormalID; + WPARAM checkboxExportEmissiveID; + WPARAM checkboxSpecularHighlightID; + WPARAM textInputNumOfThreadsID; + WPARAM comboTextureFormatID; + WPARAM comboTextureTypeID; + WPARAM comboTextureSizeID; + WPARAM comboTextureNormalsID; + WPARAM buttonExportTexturesID; + WPARAM progressbarID; + WPARAM labelProgressTextID; + WPARAM buttonExportObjectID; + WPARAM checkboxExportAttachmentsID; + + HWND buttonSelectInputFolder; + HWND buttonSelectInputData; + HWND buttonSelectOutputFolder; + HWND buttonSelectTextureFolder; + HWND textInputInputFolder; + HWND textInputInputData; + HWND textInputOutputFolder; + HWND textInputTextureFolder; + HWND comboUvMapSettings; + HWND checkboxExportMaterials; + HWND checkboxExportDiffuse; + HWND checkboxExportAlpha; + HWND checkboxExportNormal; + HWND checkboxExportEmissive; + HWND textInputNumOfThreads; + HWND comboTextureFormat; + HWND comboTextureType; + HWND comboTextureSize; + HWND comboTextureNormals; + HWND buttonExportTextures; + HWND progressbar; + HWND labelProgressText; + HWND buttonExportObject; + HWND checkboxSpecularHighlight; + HWND checkboxExportAttachments; + }; +}; + +#endif diff --git a/third-party-libs/FragmentFramework/LICENSE b/third-party-libs/FragmentFramework/LICENSE deleted file mode 100644 index 1691378..0000000 --- a/third-party-libs/FragmentFramework/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Matus Novak matusnov@gmail.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/third-party-libs/FragmentFramework/README.md b/third-party-libs/FragmentFramework/README.md deleted file mode 100644 index 99578a8..0000000 --- a/third-party-libs/FragmentFramework/README.md +++ /dev/null @@ -1,41 +0,0 @@ -![Logo](http://matusnovak.github.io/fragmentframework/images/ffw-logo-web.png) -================= - -__More information including documentation and tutorials can be found at:__ __[http://matusnovak.github.io/fragmentframework/](http://matusnovak.github.io/fragmentframework/)__ - -__This project is work in progress and is not yet ready for stable release. Use at your own risk.__ - -FragmentFramework (or FFW for short) is an open source framework written in C++ designed for developing applications. This framework provides basic simple structure to write things easily with less code. Anyone can use this framework without greater knowledge of programming. This framework provides functions which can be used as a building blocks for applications of any kind. The core functions should provide sufficient exploitation for any problem. - -This framework is currently supported __only on Windows__ platform (Linux support is planned). In order to use this framework you need MinGW-w32 version 4.9.2 or higher. - -__This framework wraps together several common used libraries:__ - -* [OpenGL](http://www.opengl.org/) - 2D and 3D vector graphics -* [GLFW](http://www.glfw.org/) - Window context handling -* [Winsock](http://msdn.microsoft.com/en-us/library/windows/desktop/ms740673.aspx) - Network communication -* [OpenAL](http://kcat.strangesoft.net/openal.html) - Audio context handling (optional as addon) -* [FFmpeg](https://www.ffmpeg.org/) - For playing video files (optional as addon) -* [FreeType](http://www.freetype.org/) - True Type Font floading -* [libpng](http://www.libpng.org/pub/png/libpng.html) - Loading PNG image files -* [libtiff](http://www.libtiff.org/) - Loading TIFF image files -* [tinyxml](http://www.grinninglizard.com/tinyxml2/index.html) - Loading XML files -* [Zlib](http://www.zlib.net/) - Data compression (also required by FreeType) -* [libogg](https://xiph.org/ogg/) - Opening OGG containers -* [libvorbis](https://xiph.org/vorbis/) - Compressing and decompressing OGG audio files - -__This framework provides features such as:__ - -* __2D and 3D graphics__ - Basic shapes, shaders, buffer objects, etc... -* __Font__ - Rendering TTF fonts -* __Network__ - UDP listener and sender + TCP server and client -* __Serial__ - Serial port data sending and receiving -* __Sound__ - Sound playing (width addon) -* __Video__ - Video playing (width addon) -* __File loading__ - Loading and saving files such as: WAV, BMP, PBM, PNG, TIFF, TGA, OBJ -* __Structure parser__ - Parsing XML, INI and JSON from/to string or file -* __System spefici utilities__ - File/folder save/load dialogs, screen capture, clipboard, ... -* __Math__ - 2D, 3D, 4D vectors, matrices and quaternion -* __UserInterface__ - GUI windows including widgets -* and much more... - diff --git a/third-party-libs/FragmentFramework/include/ffw.h b/third-party-libs/FragmentFramework/include/ffw.h index a3c61ee..5045bea 100644 --- a/third-party-libs/FragmentFramework/include/ffw.h +++ b/third-party-libs/FragmentFramework/include/ffw.h @@ -28,7 +28,7 @@ #include "network/tcpServer.h" #include "network/tcpClient.h" #include "serial/serialManager.h" -#include "systemUtils/listDirectory.h" +#include "systemUtils/directory.h" #include "systemUtils/win32SysUtils.h" #include "userInterface/manager.h" #include "userInterface/window.h" diff --git a/third-party-libs/FragmentFramework/include/gl/renderUtilities.h b/third-party-libs/FragmentFramework/include/gl/renderUtilities.h index fdc130b..1a11659 100644 --- a/third-party-libs/FragmentFramework/include/gl/renderUtilities.h +++ b/third-party-libs/FragmentFramework/include/gl/renderUtilities.h @@ -12,11 +12,6 @@ #include #include "../dll.h" -#include "../graphics/shaderArcData.h" -#include "../graphics/shaderFontData.h" -#include "../graphics/shaderLineData.h" -#include "../graphics/shaderRectData.h" -#include "../graphics/shaderTextureData.h" #include "../math/math.h" #include "../graphics/graphicsShader.h" #include "../graphics/graphicsTexture2D.h" @@ -103,6 +98,11 @@ namespace ffw { @memberof renderUtilities @ingroup Core */ + void drawBox(float PosX, float PosY, float PosZ, float SizeX, float SizeY, float SizeZ, const ffw::color& Color, const ffw::mat4& ModelViewProj); + /*! + @memberof renderUtilities + @ingroup Core + */ void drawArc(float PosX, float PosY, float InnerRadius, float OuterRadius, float StartAngle, float EndAngle, const ffw::color& Color); /*! @memberof renderUtilities @@ -179,27 +179,39 @@ namespace ffw { @ingroup Core */ void drawLine(float StartX, float StartY, float EndX, float EndY, const ffw::color& Color); + /*! + @memberof renderUtilities + @ingroup Core + */ + void drawLine(float StartX, float StartY, float StartZ, float EndX, float EndY, float EndZ, const ffw::color& Color, const ffw::mat4& ModelViewProj); private: + ffw::shader boxShader; ffw::shader arcShader; ffw::shader rectShader; ffw::shader textureShader; ffw::shader fontShader; ffw::shader lineShader; + int boxShaderViewProjLoc; + int boxShaderPosLoc; + int boxShaderSizeLoc; + int boxShaderColorLoc; + bool boxShaderUniform; + int arcShaderViewLoc; int arcShaderPosLoc; int arcShaderSizeLoc; int arcShaderColorLoc; int arcShaderAngleLoc; int arcShaderStepsLoc; - bool arcShaderUniform = false; + bool arcShaderUniform; int rectShaderViewLoc; int rectShaderPosLoc; int rectShaderSizeLoc; int rectShaderColorLoc; - bool rectShaderUniform = false; + bool rectShaderUniform; int textureShaderViewLoc; int textureShaderPosLoc; @@ -208,17 +220,17 @@ namespace ffw { int textureShaderMirrorLoc; int textureShaderRstvLoc; int textureShaderColorLoc; - bool textureShaderUniform = false; + bool textureShaderUniform; int lineShaderViewLoc; int lineShaderStartLoc; int lineShaderEndLoc; int lineShaderColorLoc; - bool lineShaderUniform = true; + bool lineShaderUniform; - int arcShaderSteps = 24; + int arcShaderSteps; - bool graphicsShadersLoaded = false; + bool graphicsShadersLoaded; int mirror[2]; int viewport[4]; diff --git a/third-party-libs/FragmentFramework/include/graphics/bufferObject.h b/third-party-libs/FragmentFramework/include/graphics/bufferObject.h index 07e76be..0c6dbb5 100644 --- a/third-party-libs/FragmentFramework/include/graphics/bufferObject.h +++ b/third-party-libs/FragmentFramework/include/graphics/bufferObject.h @@ -40,13 +40,25 @@ namespace ffw { @ingroup Graphics */ - bool createBuffer(const void* data, GLsizei Size, GLenum Type); + bool createBuffer(const void* Data, GLsizei Size, GLenum Type); /*! @memberof bufferObject @ingroup Graphics */ - bool uploadData(const void* data, GLsizei Offset, GLsizei Size); + bool uploadData(const void* Data, GLsizei Offset, GLsizei Size); + /*! + @memberof bufferObject + @ingroup Graphics + + */ + bool mapBuffer(void** Pointer, GLenum Access); + /*! + @memberof bufferObject + @ingroup Graphics + + */ + bool unmapBuffer(); /*! @memberof bufferObject @ingroup Graphics @@ -118,6 +130,8 @@ namespace ffw { PFNGLBUFFERSUBDATAPROC glBufferSubData; PFNGLDELETEBUFFERSPROC glDeleteBuffers; PFNGLCOPYBUFFERSUBDATAPROC glCopyBufferSubData; + PFNGLMAPBUFFERPROC glMapBuffer; + PFNGLUNMAPBUFFERPROC glUnmapBuffer; }; }; #endif diff --git a/third-party-libs/FragmentFramework/include/graphics/graphicsShader.h b/third-party-libs/FragmentFramework/include/graphics/graphicsShader.h index adb175b..5480076 100644 --- a/third-party-libs/FragmentFramework/include/graphics/graphicsShader.h +++ b/third-party-libs/FragmentFramework/include/graphics/graphicsShader.h @@ -73,31 +73,49 @@ namespace ffw { @ingroup Graphics */ - void setAttributePointerf(GLint location, GLint size, GLsizei stride, GLsizei offset); + void setAttributePointerf(GLint Location, GLint Size, GLsizei Stride, GLsizei Offset); /*! @memberof shader @ingroup Graphics */ - void drawArrays(GLenum type, GLint first, GLsizei count); + void setAttributeDivisor(GLuint Index, GLuint Divisor); /*! @memberof shader @ingroup Graphics */ - void drawElements(GLenum type, GLsizei count, const GLvoid * indices); + void drawArrays(GLenum Mode, GLint First, GLsizei Count); /*! @memberof shader @ingroup Graphics */ - void drawElementsRange(GLenum type, GLuint start, GLuint end, GLsizei count, const GLvoid * indices); + void drawArraysInstanced(GLenum Mode, GLint First, GLsizei Count, GLsizei InstanceCount); /*! @memberof shader @ingroup Graphics */ - void drawElementsBaseVertex(GLenum type, GLsizei count, const GLvoid * indices, GLint offset); + void drawElements(GLenum Mode, GLsizei Count, GLenum Type, const GLvoid * Indices); + /*! + @memberof shader + @ingroup Graphics + + */ + void drawElementsRange(GLenum Mode, GLuint Start, GLuint End, GLsizei Count, GLenum Type, const GLvoid * Indices); + /*! + @memberof shader + @ingroup Graphics + + */ + void drawElementsBaseVertex(GLenum Mode, GLsizei Count, GLenum Type, const GLvoid * Indices, GLint Offset); + /*! + @memberof shader + @ingroup Graphics + + */ + void drawElementsInstanced(GLenum Mode, GLsizei Count, GLenum Type, const GLvoid * Indices, GLsizei InstanceCount); /*! @memberof shader @ingroup Graphics @@ -127,85 +145,85 @@ namespace ffw { @ingroup Graphics */ - void setUniform1f (GLint location, GLfloat value); + void setUniform1f(GLint location, GLfloat value); /*! @memberof shader @ingroup Graphics */ - void setUniform1fv (GLint location, GLfloat* array, GLsizei length); + void setUniform1fv(GLint location, const GLfloat* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform1i (GLint location, GLint value); + void setUniform1i(GLint location, GLint value); /*! @memberof shader @ingroup Graphics */ - void setUniform1iv (GLint location, GLint* array, GLsizei length); + void setUniform1iv(GLint location, const GLint* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform2f (GLint location, GLfloat x, GLfloat y); + void setUniform2f(GLint location, GLfloat x, GLfloat y); /*! @memberof shader @ingroup Graphics */ - void setUniform2fv (GLint location, GLfloat* array, GLsizei length); + void setUniform2fv(GLint location, const GLfloat* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform2i (GLint location, GLint x, GLint y); + void setUniform2i(GLint location, GLint x, GLint y); /*! @memberof shader @ingroup Graphics */ - void setUniform2iv (GLint location, GLint* array, GLsizei length); + void setUniform2iv(GLint location, const GLint* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform3f (GLint location, GLfloat x, GLfloat y, GLfloat z); + void setUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z); /*! @memberof shader @ingroup Graphics */ - void setUniform3fv (GLint location, GLfloat* array, GLsizei length); + void setUniform3fv(GLint location, const GLfloat* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform3i (GLint location, GLint x, GLint y, GLint z); + void setUniform3i(GLint location, GLint x, GLint y, GLint z); /*! @memberof shader @ingroup Graphics */ - void setUniform3iv (GLint location, GLint* array, GLsizei length); + void setUniform3iv(GLint location, const GLint* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform4f (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); /*! @memberof shader @ingroup Graphics */ - void setUniform4fv (GLint location, GLfloat* array, GLsizei length); + void setUniform4fv(GLint location, const GLfloat* array, GLsizei length); /*! @memberof shader @ingroup Graphics @@ -217,109 +235,109 @@ namespace ffw { @ingroup Graphics */ - void setUniform4iv (GLint location, GLint* array, GLsizei length); + void setUniform4iv(GLint location, const GLint* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform2f (GLint location, ffw::vec2f vec); + void setUniform2f(GLint location, const ffw::vec2f& vec); /*! @memberof shader @ingroup Graphics */ - void setUniform2fv (GLint location, ffw::vec2f* array, GLsizei length); + void setUniform2fv(GLint location, const ffw::vec2f* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform2i (GLint location, ffw::vec2i vec); + void setUniform2i(GLint location, const ffw::vec2i& vec); /*! @memberof shader @ingroup Graphics */ - void setUniform2iv (GLint location, ffw::vec2i* array, GLsizei length); + void setUniform2iv(GLint location, const ffw::vec2i* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform3f (GLint location, ffw::vec3f vec); + void setUniform3f(GLint location, const ffw::vec3f& vec); /*! @memberof shader @ingroup Graphics */ - void setUniform3fv (GLint location, ffw::vec3f* array, GLsizei length); + void setUniform3fv(GLint location, const ffw::vec3f* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform3i (GLint location, ffw::vec3i vec); + void setUniform3i(GLint location, const ffw::vec3i& vec); /*! @memberof shader @ingroup Graphics */ - void setUniform3iv (GLint location, ffw::vec3i* array, GLsizei length); + void setUniform3iv(GLint location, const ffw::vec3i* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform4f (GLint location, ffw::vec4f vec); + void setUniform4f(GLint location, const ffw::vec4f& vec); /*! @memberof shader @ingroup Graphics */ - void setUniform4fv (GLint location, ffw::vec4f* array, GLsizei length); + void setUniform4fv(GLint location, const ffw::vec4f* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform4i (GLint location, ffw::vec4i vec); + void setUniform4i(GLint location, const ffw::vec4i& vec); /*! @memberof shader @ingroup Graphics */ - void setUniform4iv (GLint location, ffw::vec4i* array, GLsizei length); + void setUniform4iv(GLint location, const ffw::vec4i* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniform4f (GLint location, ffw::color vec); + void setUniform4f(GLint location, const ffw::color& vec); /*! @memberof shader @ingroup Graphics */ - void setUniform4fv (GLint location, ffw::color* array, GLsizei length); + void setUniform4fv(GLint location, const ffw::color* array, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniformMatrix2fv (GLint location, GLfloat* mat, GLsizei length); + void setUniformMatrix2fv(GLint location, const GLfloat* mat, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniformMatrix3fv (GLint location, GLfloat* mat, GLsizei length); + void setUniformMatrix3fv(GLint location, const GLfloat* mat, GLsizei length); /*! @memberof shader @ingroup Graphics */ - void setUniformMatrix4fv (GLint location, GLfloat* mat, GLsizei length); + void setUniformMatrix4fv(GLint location, const GLfloat* mat, GLsizei length); private: // Compile shader @@ -367,6 +385,9 @@ namespace ffw { PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements; PFNGLDRAWELEMENTSBASEVERTEXPROC glDrawElementsBaseVertex; + PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced; + PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced; + PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor; PFNGLUNIFORM1FPROC glUniform1f; PFNGLUNIFORM1FVPROC glUniform1fv; PFNGLUNIFORM1IPROC glUniform1i; diff --git a/third-party-libs/FragmentFramework/include/graphics/graphicsTextureBase.h b/third-party-libs/FragmentFramework/include/graphics/graphicsTextureBase.h index 9b7660d..6cfd899 100644 --- a/third-party-libs/FragmentFramework/include/graphics/graphicsTextureBase.h +++ b/third-party-libs/FragmentFramework/include/graphics/graphicsTextureBase.h @@ -210,30 +210,56 @@ namespace ffw { @memberof textureBase @ingroup Graphics - @brief Sets the minification filtering flag + @brief Sets the environment mode - @details Use GL_LINEAR or GL_NEAREST as input. + @warning Render context must be active and be on + same thread before calling this function. + + @param [in] Target Target parameter + @param [in] Name Name of the parameter + @param [in] Value Value + */ + void setEnvParami(GLenum Target, GLenum Name, GLint Value); + /*! + @memberof textureBase + @ingroup Graphics + + @brief Sets the environment mode @warning Render context must be active and be on same thread before calling this function. - @param [in] Filtering Filtering flag + @param [in] Target Target parameter + @param [in] Name Name of the parameter + @param [in] Value Value */ - void setMinFiltering(GLenum Filtering); + void setEnvParamf(GLenum Target, GLenum Name, GLfloat Value); /*! @memberof textureBase @ingroup Graphics - @brief Sets the magnification filtering flag + @brief Sets the texture parameter + + @warning Render context must be active and be on + same thread before calling this function. + + @param [in] Name Name of the parameter + @param [in] Value Value + */ + void setTexParami(GLenum Name, GLint Value); + /*! + @memberof textureBase + @ingroup Graphics - @details Use GL_LINEAR or GL_NEAREST as input. + @brief Sets the texture parameter @warning Render context must be active and be on same thread before calling this function. - @param [in] Filtering Filtering flag + @param [in] Name Name of the parameter + @param [in] Value Value */ - void setMagFiltering(GLenum Filtering); + void setTexParamf(GLenum Name, GLfloat Value); /*! @memberof textureBase @ingroup Graphics diff --git a/third-party-libs/FragmentFramework/include/graphics/shaderBoxData.h b/third-party-libs/FragmentFramework/include/graphics/shaderBoxData.h new file mode 100644 index 0000000..3bbdef9 --- /dev/null +++ b/third-party-libs/FragmentFramework/include/graphics/shaderBoxData.h @@ -0,0 +1,32 @@ +/* +* This file is part of FragmentFramework framework. +* Copyright (C) 2013-2015 by Matus Novak matusnov@gmail.com +* Licensed under the MIT License +*/ + +#ifndef FFW_SHADER_BOX_DATA +#define FFW_SHADER_BOX_DATA + +#include + +/*! + @ingroup Graphics +*/ +namespace ffw { + /*! + @memberof ffw + @ingroup Graphics + @brief Vertex data for basic rectangle shader + */ + extern std::string boxShaderVertData; + /*! + @memberof ffw + @ingroup Graphics + @brief Fragment data for basic rectangle shader + */ + extern std::string boxShaderFragData; +}; + +#endif + + diff --git a/third-party-libs/FragmentFramework/include/math/baseFunctions.h b/third-party-libs/FragmentFramework/include/math/baseFunctions.h index 368a190..1d329dd 100644 --- a/third-party-libs/FragmentFramework/include/math/baseFunctions.h +++ b/third-party-libs/FragmentFramework/include/math/baseFunctions.h @@ -408,7 +408,7 @@ namespace ffw{ @ingroup Math @inline */ - mat4 makeRotationMatrix(quaternion& Q); + mat4 makeRotationMatrix(const quaternion& Q); }; #include "baseFunctions.inl" diff --git a/third-party-libs/FragmentFramework/include/math/baseFunctions.inl b/third-party-libs/FragmentFramework/include/math/baseFunctions.inl index bedd6ce..0eeee64 100644 --- a/third-party-libs/FragmentFramework/include/math/baseFunctions.inl +++ b/third-party-libs/FragmentFramework/include/math/baseFunctions.inl @@ -291,7 +291,7 @@ inline uint32_t ffw::nextPowOfTwo(uint32_t Value){ } ///============================================================================= -inline ffw::mat4 ffw::makeRotationMatrix(ffw::quaternion& Q){ +inline ffw::mat4 ffw::makeRotationMatrix(const ffw::quaternion& Q){ mat4 m; m[0] = 1.0f - 2.0f * (Q.y * Q.y + Q.z * Q.z); m[4] = 2.0f * (Q.x * Q.y + Q.z * Q.w); m[8] = 2.0f * (Q.x * Q.z - Q.y * Q.w); diff --git a/third-party-libs/FragmentFramework/include/math/mat4.h b/third-party-libs/FragmentFramework/include/math/mat4.h index dc3a176..e027041 100644 --- a/third-party-libs/FragmentFramework/include/math/mat4.h +++ b/third-party-libs/FragmentFramework/include/math/mat4.h @@ -77,25 +77,67 @@ namespace ffw { @ingroup Math @inline */ - mat4& operator = (const ffw::mat4 &m); + mat4& operator = (const mat4& m); /*! @memberof mat4 @ingroup Math @inline */ - mat4 operator * (const ffw::mat4 &m) const; + mat4 operator * (const mat4& m) const; /*! @memberof mat4 @ingroup Math @inline */ - mat4& operator *= (const ffw::mat4 &m); + mat4& operator *= (const mat4& m); + /*! + @memberof mat4 + @ingroup Math + @inline + */ + template vec4 operator * (const vec4& V) const; /*! @memberof mat4 @ingroup Math @inline */ float& operator [] (int x); + /*! + @memberof mat4 + @ingroup Math + @inline + */ + const float& operator [] (int x) const; + /*! + @memberof mat4 + @ingroup Math + @inline + */ + mat4& rotate(const quaternion& Q); + /*! + @memberof mat4 + @ingroup Math + @inline + */ + mat4& translate(float X, float Y, float Z); + /*! + @memberof mat4 + @ingroup Math + @inline + */ + mat4& scale(float X, float Y, float Z); + /*! + @memberof mat4 + @ingroup Math + @inline + */ + mat4& transpose(); + /*! + @memberof mat4 + @ingroup Math + @inline + */ + mat4& inverse(); }; }; #endif diff --git a/third-party-libs/FragmentFramework/include/math/mat4.inl b/third-party-libs/FragmentFramework/include/math/mat4.inl index 37080c5..4609a1f 100644 --- a/third-party-libs/FragmentFramework/include/math/mat4.inl +++ b/third-party-libs/FragmentFramework/include/math/mat4.inl @@ -120,9 +120,146 @@ inline ffw::mat4& ffw::mat4::operator *= (const ffw::mat4 &m){ return *this; } +///============================================================================= +template inline ffw::vec4 ffw::mat4::operator * (const ffw::vec4& V) const { + ffw::vec4f r; + r.x = V.x*ptr[0] + V.y*ptr[4] + V.z*ptr[8] + V.w*ptr[12]; + r.y = V.x*ptr[1] + V.y*ptr[5] + V.z*ptr[9] + V.w*ptr[13]; + r.z = V.x*ptr[2] + V.y*ptr[6] + V.z*ptr[10] + V.w*ptr[14]; + r.w = V.x*ptr[3] + V.y*ptr[7] + V.z*ptr[11] + V.w*ptr[15]; + return r; +} + ///============================================================================= inline float& ffw::mat4::operator [] (int x){ return ptr[x]; } +///============================================================================= +inline const float& ffw::mat4::operator [] (int x) const { + return ptr[x]; +} + +///============================================================================= +inline ffw::mat4& ffw::mat4::rotate(const ffw::quaternion& Q){ + mat4 m; + + m[0] = 1.0f - 2.0f * (Q.y * Q.y + Q.z * Q.z); m[4] = 2.0f * (Q.x * Q.y + Q.z * Q.w); m[8] = 2.0f * (Q.x * Q.z - Q.y * Q.w); + m[1] = 2.0f * (Q.x * Q.y - Q.z * Q.w); m[5] = 1.0f - 2.0f * (Q.x * Q.x + Q.z * Q.z); m[9] = 2.0f * (Q.y * Q.z + Q.x * Q.w); + m[2] = 2.0f * (Q.x * Q.z + Q.y * Q.w); m[6] = 2.0f * (Q.y * Q.z - Q.x * Q.w); m[10] = 1.0f - 2.0f * (Q.x * Q.x + Q.y * Q.y); + + (*this) *= m; + + return *this; +} + +///============================================================================= +inline ffw::mat4& ffw::mat4::translate(float X, float Y, float Z){ + ptr[12] += X; + ptr[13] += Y; + ptr[14] += Z; + + return *this; +} + +///============================================================================= +inline ffw::mat4& ffw::mat4::scale(float X, float Y, float Z){ + mat4 m; + + m[0] = X; + m[5] = Y; + m[10] = Z; + + (*this) *= m; + + return *this; +} + +///============================================================================= +inline ffw::mat4& ffw::mat4::transpose(){ + float m04 = ptr[4]; + float m08 = ptr[8]; + float m09 = ptr[9]; + float m12 = ptr[12]; + float m13 = ptr[13]; + float m14 = ptr[14]; + + ptr[4] = ptr[1]; + ptr[8] = ptr[2]; + ptr[9] = ptr[6]; + ptr[12] = ptr[3]; + ptr[13] = ptr[7]; + ptr[14] = ptr[11]; + + ptr[1] = m04; + ptr[2] = m08; + ptr[6] = m09; + ptr[3] = m12; + ptr[7] = m13; + ptr[11] = m14; + + return *this; +} + +///============================================================================= +inline ffw::mat4& ffw::mat4::inverse(){ + float inv[16]; + + inv[0] = ptr[5] * ptr[10] * ptr[15] - ptr[5] * ptr[11] * ptr[14] - ptr[9] * ptr[6] * ptr[15] + ptr[9] * ptr[7] * ptr[14] +ptr[13] * ptr[6] * ptr[11] - ptr[13] * ptr[7] * ptr[10]; + inv[4] = -ptr[4] * ptr[10] * ptr[15] + ptr[4] * ptr[11] * ptr[14] + ptr[8] * ptr[6] * ptr[15] - ptr[8] * ptr[7] * ptr[14] - ptr[12] * ptr[6] * ptr[11] + ptr[12] * ptr[7] * ptr[10]; + inv[8] = ptr[4] * ptr[9] * ptr[15] - ptr[4] * ptr[11] * ptr[13] - ptr[8] * ptr[5] * ptr[15] + ptr[8] * ptr[7] * ptr[13] + ptr[12] * ptr[5] * ptr[11] - ptr[12] * ptr[7] * ptr[9]; + inv[12] = -ptr[4] * ptr[9] * ptr[14] + ptr[4] * ptr[10] * ptr[13] +ptr[8] * ptr[5] * ptr[14] - ptr[8] * ptr[6] * ptr[13] - ptr[12] * ptr[5] * ptr[10] + ptr[12] * ptr[6] * ptr[9]; + inv[1] = -ptr[1] * ptr[10] * ptr[15] + ptr[1] * ptr[11] * ptr[14] + ptr[9] * ptr[2] * ptr[15] - ptr[9] * ptr[3] * ptr[14] - ptr[13] * ptr[2] * ptr[11] + ptr[13] * ptr[3] * ptr[10]; + inv[5] = ptr[0] * ptr[10] * ptr[15] - ptr[0] * ptr[11] * ptr[14] - ptr[8] * ptr[2] * ptr[15] + ptr[8] * ptr[3] * ptr[14] + ptr[12] * ptr[2] * ptr[11] - ptr[12] * ptr[3] * ptr[10]; + inv[9] = -ptr[0] * ptr[9] * ptr[15] + ptr[0] * ptr[11] * ptr[13] + ptr[8] * ptr[1] * ptr[15] - ptr[8] * ptr[3] * ptr[13] - ptr[12] * ptr[1] * ptr[11] + ptr[12] * ptr[3] * ptr[9]; + inv[13] = ptr[0] * ptr[9] * ptr[14] - ptr[0] * ptr[10] * ptr[13] - ptr[8] * ptr[1] * ptr[14] + ptr[8] * ptr[2] * ptr[13] + ptr[12] * ptr[1] * ptr[10] - ptr[12] * ptr[2] * ptr[9]; + inv[2] = ptr[1] * ptr[6] * ptr[15] - ptr[1] * ptr[7] * ptr[14] - ptr[5] * ptr[2] * ptr[15] + ptr[5] * ptr[3] * ptr[14] + ptr[13] * ptr[2] * ptr[7] - ptr[13] * ptr[3] * ptr[6]; + inv[6] = -ptr[0] * ptr[6] * ptr[15] + ptr[0] * ptr[7] * ptr[14] + ptr[4] * ptr[2] * ptr[15] - ptr[4] * ptr[3] * ptr[14] - ptr[12] * ptr[2] * ptr[7] + ptr[12] * ptr[3] * ptr[6]; + inv[10] = ptr[0] * ptr[5] * ptr[15] - ptr[0] * ptr[7] * ptr[13] - ptr[4] * ptr[1] * ptr[15] + ptr[4] * ptr[3] * ptr[13] + ptr[12] * ptr[1] * ptr[7] - ptr[12] * ptr[3] * ptr[5]; + inv[14] = -ptr[0] * ptr[5] * ptr[14] + ptr[0] * ptr[6] * ptr[13] + ptr[4] * ptr[1] * ptr[14] - ptr[4] * ptr[2] * ptr[13] - ptr[12] * ptr[1] * ptr[6] + ptr[12] * ptr[2] * ptr[5]; + inv[3] = -ptr[1] * ptr[6] * ptr[11] + ptr[1] * ptr[7] * ptr[10] + ptr[5] * ptr[2] * ptr[11] - ptr[5] * ptr[3] * ptr[10] - ptr[9] * ptr[2] * ptr[7] + ptr[9] * ptr[3] * ptr[6]; + inv[7] = ptr[0] * ptr[6] * ptr[11] - ptr[0] * ptr[7] * ptr[10] - ptr[4] * ptr[2] * ptr[11] + ptr[4] * ptr[3] * ptr[10] + ptr[8] * ptr[2] * ptr[7] - ptr[8] * ptr[3] * ptr[6]; + inv[11] = -ptr[0] * ptr[5] * ptr[11] + ptr[0] * ptr[7] * ptr[9] + ptr[4] * ptr[1] * ptr[11] - ptr[4] * ptr[3] * ptr[9] - ptr[8] * ptr[1] * ptr[7] + ptr[8] * ptr[3] * ptr[5]; + inv[15] = ptr[0] * ptr[5] * ptr[10] - ptr[0] * ptr[6] * ptr[9] - ptr[4] * ptr[1] * ptr[10] + ptr[4] * ptr[2] * ptr[9] + ptr[8] * ptr[1] * ptr[6] - ptr[8] * ptr[2] * ptr[5]; + + float det = ptr[0] * inv[0] + ptr[1] * inv[4] + ptr[2] * inv[8] + ptr[3] * inv[12]; + + det = 1.0 / det; + + for(int i = 0; i < 16; i++)ptr[i] = inv[i] * det; + + /*float det = + ptr[3] *ptr[6] *ptr[9] *ptr[12] - ptr[2] *ptr[7] *ptr[9] *ptr[12] - ptr[3] *ptr[5] *ptr[10]*ptr[12] + ptr[1] *ptr[7] *ptr[10]*ptr[12] + + ptr[2] *ptr[5] *ptr[11]*ptr[12] - ptr[1] *ptr[6] *ptr[11]*ptr[12] - ptr[3] *ptr[6] *ptr[8] *ptr[13] + ptr[2] *ptr[7] *ptr[8] *ptr[13] + + ptr[3] *ptr[4] *ptr[10]*ptr[13] - ptr[0] *ptr[7] *ptr[10]*ptr[13] - ptr[2] *ptr[4] *ptr[11]*ptr[13] + ptr[0] *ptr[6] *ptr[11]*ptr[13] + + ptr[3] *ptr[5] *ptr[8] *ptr[14] - ptr[1] *ptr[7] *ptr[8] *ptr[14] - ptr[3] *ptr[4] *ptr[9] *ptr[14] + ptr[0] *ptr[7] *ptr[9] *ptr[14] + + ptr[1] *ptr[4] *ptr[11]*ptr[14] - ptr[0] *ptr[5] *ptr[11]*ptr[14] - ptr[2] *ptr[5] *ptr[8] *ptr[15] + ptr[1] *ptr[6] *ptr[8] *ptr[15] + + ptr[2] *ptr[4] *ptr[9] *ptr[15] - ptr[0] *ptr[6] *ptr[9] *ptr[15] - ptr[1] *ptr[4] *ptr[10]*ptr[15] + ptr[0] *ptr[5] *ptr[10]*ptr[15]; + + b[0] = ptr[6] *ptr[11]*ptr[13] - ptr[7] *ptr[10]*ptr[13] + ptr[7] *ptr[9] *ptr[14] - ptr[5] *ptr[11]*ptr[14] - ptr[6] *ptr[9] *ptr[15] + ptr[5] *ptr[10]*ptr[15]; + b[1] = ptr[3] *ptr[10]*ptr[13] - ptr[2] *ptr[11]*ptr[13] - ptr[3] *ptr[9] *ptr[14] + ptr[1] *ptr[11]*ptr[14] + ptr[2] *ptr[9] *ptr[15] - ptr[1] *ptr[10]*ptr[15]; + b[2] = ptr[2] *ptr[7] *ptr[13] - ptr[3] *ptr[6] *ptr[13] + ptr[3] *ptr[5] *ptr[14] - ptr[1] *ptr[7] *ptr[14] - ptr[2] *ptr[5] *ptr[15] + ptr[1] *ptr[6] *ptr[15]; + b[3] = ptr[3] *ptr[6] *ptr[9] - ptr[2] *ptr[7] *ptr[9] - ptr[3] *ptr[5] *ptr[10] + ptr[1] *ptr[7] *ptr[10] + ptr[2] *ptr[5] *ptr[11] - ptr[1] *ptr[6] *ptr[11]; + b[4] = ptr[7] *ptr[10]*ptr[12] - ptr[6] *ptr[11]*ptr[12] - ptr[7] *ptr[8] *ptr[14] + ptr[4] *ptr[11]*ptr[14] + ptr[6] *ptr[8] *ptr[15] - ptr[4] *ptr[10]*ptr[15]; + b[5] = ptr[2] *ptr[11]*ptr[12] - ptr[3] *ptr[10]*ptr[12] + ptr[3] *ptr[8] *ptr[14] - ptr[0] *ptr[11]*ptr[14] - ptr[2] *ptr[8] *ptr[15] + ptr[0] *ptr[10]*ptr[15]; + b[6] = ptr[3] *ptr[6] *ptr[12] - ptr[2] *ptr[7] *ptr[12] - ptr[3] *ptr[4] *ptr[14] + ptr[0] *ptr[7] *ptr[14] + ptr[2] *ptr[4] *ptr[15] - ptr[0] *ptr[6] *ptr[15]; + b[7] = ptr[2] *ptr[7] *ptr[8] - ptr[3] *ptr[6] *ptr[8] + ptr[3] *ptr[4] *ptr[10] - ptr[0] *ptr[7] *ptr[10] - ptr[2] *ptr[4] *ptr[11] + ptr[0] *ptr[6] *ptr[11]; + b[8] = ptr[5] *ptr[11]*ptr[12] - ptr[7] *ptr[9] *ptr[12] + ptr[7] *ptr[8] *ptr[13] - ptr[4] *ptr[11]*ptr[13] - ptr[5] *ptr[8] *ptr[15] + ptr[4] *ptr[9] *ptr[15]; + b[9] = ptr[3] *ptr[9] *ptr[12] - ptr[1] *ptr[11]*ptr[12] - ptr[3] *ptr[8] *ptr[13] + ptr[0] *ptr[11]*ptr[13] + ptr[1] *ptr[8] *ptr[15] - ptr[0] *ptr[9] *ptr[15]; + b[10] = ptr[1] *ptr[7] *ptr[12] - ptr[3] *ptr[5] *ptr[12] + ptr[3] *ptr[4] *ptr[13] - ptr[0] *ptr[7] *ptr[13] - ptr[1] *ptr[4] *ptr[15] + ptr[0] *ptr[5] *ptr[15]; + b[11] = ptr[3] *ptr[5] *ptr[8] - ptr[1] *ptr[7] *ptr[8] - ptr[3] *ptr[4] *ptr[9] + ptr[0] *ptr[7] *ptr[9] + ptr[1] *ptr[4] *ptr[11] - ptr[0] *ptr[5] *ptr[11]; + b[12] = ptr[6] *ptr[9] *ptr[12] - ptr[5] *ptr[10]*ptr[12] - ptr[6] *ptr[8] *ptr[13] + ptr[4] *ptr[10]*ptr[13] + ptr[5] *ptr[8] *ptr[14] - ptr[4] *ptr[9] *ptr[14]; + b[13] = ptr[1] *ptr[10]*ptr[12] - ptr[2] *ptr[9] *ptr[12] + ptr[2] *ptr[8] *ptr[13] - ptr[0] *ptr[10]*ptr[13] - ptr[1] *ptr[8] *ptr[14] + ptr[0] *ptr[9] *ptr[14]; + b[14] = ptr[2] *ptr[5] *ptr[12] - ptr[1] *ptr[6] *ptr[12] - ptr[2] *ptr[4] *ptr[13] + ptr[0] *ptr[6] *ptr[13] + ptr[1] *ptr[4] *ptr[14] - ptr[0] *ptr[5] *ptr[14]; + b[15] = ptr[1] *ptr[6] *ptr[8] - ptr[2] *ptr[5] *ptr[8] + ptr[2] *ptr[4] *ptr[9] - ptr[0] *ptr[6] *ptr[9] - ptr[1] *ptr[4] *ptr[10] + ptr[0] *ptr[5] *ptr[10]; + + det = 1.0 / det; + + for(int i = 0; i < 16; i++){ + ptr[i] = b[i] * det; + }*/ + + return *this; +} + #endif diff --git a/third-party-libs/FragmentFramework/include/math/math.h b/third-party-libs/FragmentFramework/include/math/math.h index 5f3d715..74a95e9 100644 --- a/third-party-libs/FragmentFramework/include/math/math.h +++ b/third-party-libs/FragmentFramework/include/math/math.h @@ -19,11 +19,11 @@ @brief Functions and structures for mathematics */ +#include "quaternion.h" #include "vec2.h" #include "vec3.h" #include "vec4.h" #include "mat4.h" -#include "quaternion.h" #include "color.h" #include "var.h" #include "range.h" @@ -37,51 +37,6 @@ namespace ffw { extern uint32_t randY; extern uint32_t randZ; extern uint32_t randW; - /*! - @memberof ffw - @ingroup Math - */ - typedef vec2 vec2f; - /*! - @memberof ffw - @ingroup Math - */ - typedef vec2 vec2i; - /*! - @memberof ffw - @ingroup Math - */ - typedef vec2 vec2d; - /*! - @memberof ffw - @ingroup Math - */ - typedef vec3 vec3f; - /*! - @memberof ffw - @ingroup Math - */ - typedef vec3 vec3i; - /*! - @memberof ffw - @ingroup Math - */ - typedef vec3 vec3d; - /*! - @memberof ffw - @ingroup Math - */ - typedef vec4 vec4f; - /*! - @memberof ffw - @ingroup Math - */ - typedef vec4 vec4i; - /*! - @memberof ffw - @ingroup Math - */ - typedef vec4 vec4d; }; #include "baseFunctions.h" diff --git a/third-party-libs/FragmentFramework/include/math/quaternion.h b/third-party-libs/FragmentFramework/include/math/quaternion.h index cde8918..bd849f8 100644 --- a/third-party-libs/FragmentFramework/include/math/quaternion.h +++ b/third-party-libs/FragmentFramework/include/math/quaternion.h @@ -14,7 +14,7 @@ namespace ffw { /*! @memberof ffw @ingroup Math - + @brief Quaternion */ struct quaternion { @@ -22,28 +22,28 @@ namespace ffw { /*! @memberof quaternion @ingroup Math - + @brief X component */ float x; /*! @memberof quaternion @ingroup Math - + @brief Y component */ float y; /*! @memberof quaternion @ingroup Math - + @brief Z component */ float z; /*! @memberof quaternion @ingroup Math - + @brief W component */ float w; @@ -51,9 +51,9 @@ namespace ffw { @memberof quaternion @ingroup Math @inline - + @brief Constructor - + @details Sets the quaternion to (0.0f, 0.0f, 0.0f, 1.0f) */ quaternion(); @@ -61,9 +61,9 @@ namespace ffw { @memberof quaternion @ingroup Math @inline - + @brief Constructor - + @param [in] X Component X @param [in] Y Component Y @param [in] Z Component Z @@ -74,11 +74,11 @@ namespace ffw { @memberof quaternion @ingroup Math @inline - + @brief Constructor - + @details Copies value from another quaternion. - + @param [in] V Another quaternion */ quaternion(const quaternion& Quat); @@ -86,24 +86,24 @@ namespace ffw { @memberof quaternion @ingroup Math @inline - + @brief Sets the quaternion - + @param [in] X Component X @param [in] Y Component Y @param [in] Z Component Z @param [in] W Component W */ - void set(const float X, const float Y, const float Z, const float W); + void set(float X, float Y, float Z, float W); /*! @memberof quaternion @ingroup Math @inline - + @brief Sets the quaternion - + @details Copies value from another quaternion. - + @param [in] V Another quaternion */ void set(const quaternion& Quat); @@ -111,73 +111,43 @@ namespace ffw { @memberof quaternion @ingroup Math @inline - - @brief Rotates the quaternion by degress - - @details Quaternion is automaticaly normalized after this operation! - - @param [in] Deg Degrees - @param [in] Axis Axis of rotation - - @return Reference to object - */ - quaternion& rotate(const float Deg, const ffw::vec3& Axis); - /*! - @memberof quaternion - @ingroup Math - @inline - + @brief Rotates the quaternion by degress - + @details Quaternion is automaticaly normalized after this operation! - + @param [in] Deg Degrees @param [in] X X component of axis of rotation @param [in] Y Y component of axis of rotation @param [in] Z Z component of axis of rotation - - @return Reference to object - */ - quaternion& rotate(const float Deg, const float X, const float Y, const float Z); - /*! - @memberof quaternion - @ingroup Math - @inline - - @brief Rotates the quaternion by radians - - @details Quaternion is automaticaly normalized after this operation! - - @param [in] Rad Radians - @param [in] Axis Axis of rotation - + @return Reference to object */ - quaternion& rotateRad(const float Rad, const ffw::vec3& Axis); + quaternion& rotate(float Deg, float X, float Y, float Z); /*! @memberof quaternion @ingroup Math @inline - + @brief Rotates the quaternion by radians - + @details Quaternion is automaticaly normalized after this operation! - + @param [in] Rad Radians @param [in] X X component of axis of rotation @param [in] Y Y component of axis of rotation @param [in] Z Z component of axis of rotation - + @return Reference to object */ - quaternion& rotateRad(const float Rad, const float X, const float Y, const float Z); + quaternion& rotateRad(float Rad, float X, float Y, float Z); /*! @memberof quaternion @ingroup Math @inline - + @brief Multiplication - + @return Product of multiplication */ quaternion operator * (const quaternion& Quat) const; @@ -185,9 +155,9 @@ namespace ffw { @memberof quaternion @ingroup Math @inline - + @brief Multiplication assigment - + @return Reference to object */ quaternion& operator *= (const quaternion& Quat); @@ -195,7 +165,7 @@ namespace ffw { @memberof quaternion @ingroup Math @inline - + @brief Normalizes the quaternion */ void normalize(); @@ -203,9 +173,9 @@ namespace ffw { @memberof quaternion @ingroup Math @inline - + @brief Get euler angles of quaterion - + @param [out] Roll Roll in degrees @param [out] Pitch Pitch in degrees @param [out] Yaw Yaw in degrees @@ -215,3 +185,5 @@ namespace ffw { }; #endif + +#include "quaternion.inl" diff --git a/third-party-libs/FragmentFramework/include/math/quaternion.inl b/third-party-libs/FragmentFramework/include/math/quaternion.inl index ffa58fd..29406d5 100644 --- a/third-party-libs/FragmentFramework/include/math/quaternion.inl +++ b/third-party-libs/FragmentFramework/include/math/quaternion.inl @@ -8,7 +8,7 @@ #define FFW_QUATERNION_INL ///============================================================================= -inline ffw::uaternion::uaternion(){ +inline ffw::quaternion::quaternion(){ x = 0.0f; y = 0.0f; z = 0.0f; @@ -16,7 +16,7 @@ inline ffw::uaternion::uaternion(){ } ///============================================================================= -inline ffw::uaternion::quaternion(float X, float Y, float Z, float W){ +inline ffw::quaternion::quaternion(float X, float Y, float Z, float W){ x = X; y = Y; z = Z; @@ -24,7 +24,7 @@ inline ffw::uaternion::quaternion(float X, float Y, float Z, float W){ } ///============================================================================= -inline ffw::uaternion::quaternion(const quaternion& Quat){ +inline ffw::quaternion::quaternion(const quaternion& Quat){ x = Quat.x; y = Quat.y; z = Quat.z; @@ -32,7 +32,7 @@ inline ffw::uaternion::quaternion(const quaternion& Quat){ } ///============================================================================= -inline void ffw::uaternion::set(const float X, const float Y, const float Z, const float W){ +inline void ffw::quaternion::set(float X, float Y, float Z, float W){ x = X; y = Y; z = Z; @@ -40,7 +40,7 @@ inline void ffw::uaternion::set(const float X, const float Y, const float Z, con } ///============================================================================= -inline void ffw::uaternion::set(const quaternion& Quat){ +inline void ffw::quaternion::set(const quaternion& Quat){ x = Quat.x; y = Quat.y; z = Quat.z; @@ -48,20 +48,8 @@ inline void ffw::uaternion::set(const quaternion& Quat){ } ///============================================================================= -inline ffw::uaternion::quaternion& ffw::uaternion::rotate(const float Deg, const ffw::vec3& Axis){ - float angle = deg*DEG_TO_RAD; - double result = sin(angle/2.0); - x = Axis.x * result; - y = Axis.y * result; - z = Axis.z * result; - w = cos(angle/2.0); - normalize(); - return *this; -} - -///============================================================================= -inline ffw::uaternion::quaternion& ffw::uaternion::rotate(const float Deg, const float Z, const float Y, const float Z){ - float angle = deg*DEG_TO_RAD; +inline ffw::quaternion& ffw::quaternion::rotate(float Deg, float X, float Y, float Z){ + float angle = Deg*DEG_TO_RAD; double result = sin(angle/2.0); x = X * result; y = Y * result; @@ -72,18 +60,7 @@ inline ffw::uaternion::quaternion& ffw::uaternion::rotate(const float Deg, const } ///============================================================================= -inline ffw::uaternion::quaternion& ffw::uaternion::rotateRad(const float Rad, const ffw::vec3& Axis){ - double result = sin(Rad/2.0); - x = Axis.x * result; - y = Axis.y * result; - z = Axis.z * result; - w = cos(Rad/2.0); - normalize(); - return *this; -} - -///============================================================================= -inline ffw::uaternion::quaternion& ffw::uaternion::rotateRad(const float Rad, const float Z, const float Y, const float Z){ +inline ffw::quaternion& ffw::quaternion::rotateRad(float Rad, float X, float Y, float Z){ double result = sin(Rad/2.0); x = X * result; y = Y * result; @@ -94,23 +71,23 @@ inline ffw::uaternion::quaternion& ffw::uaternion::rotateRad(const float Rad, co } ///============================================================================= -inline ffw::uaternion::quaternion ffw::uaternion::operator * (const quaternion& Quat) const{ +inline ffw::quaternion ffw::quaternion::operator * (const quaternion& Quat) const{ quaternion result; - result.x = q.w*x + q.x*w + q.y*z - q.z*y; - result.y = q.w*y - q.x*z + q.y*w + q.z*x; - result.z = q.w*z + q.x*y - q.y*x + q.z*w; - result.w = q.w*w - q.x*x - q.y*y - q.z*z; + result.x = Quat.w*x + Quat.x*w + Quat.y*z - Quat.z*y; + result.y = Quat.w*y - Quat.x*z + Quat.y*w + Quat.z*x; + result.z = Quat.w*z + Quat.x*y - Quat.y*x + Quat.z*w; + result.w = Quat.w*w - Quat.x*x - Quat.y*y - Quat.z*z; return result; } ///============================================================================= -inline ffw::uaternion::quaternion& ffw::uaternion::operator *= (const quaternion& Quat){ - float rx = q.w*x + q.x*w + q.y*z - q.z*y; - float ry = q.w*y - q.x*z + q.y*w + q.z*x; - float rz = q.w*z + q.x*y - q.y*x + q.z*w; - float rw = q.w*w - q.x*x - q.y*y - q.z*z; +inline ffw::quaternion& ffw::quaternion::operator *= (const quaternion& Quat){ + float rx = Quat.w*x + Quat.x*w + Quat.y*z - Quat.z*y; + float ry = Quat.w*y - Quat.x*z + Quat.y*w + Quat.z*x; + float rz = Quat.w*z + Quat.x*y - Quat.y*x + Quat.z*w; + float rw = Quat.w*w - Quat.x*x - Quat.y*y - Quat.z*z; x = rx; y = ry; @@ -121,7 +98,7 @@ inline ffw::uaternion::quaternion& ffw::uaternion::operator *= (const quaternion } ///============================================================================= -inline void ffw::uaternion::normalize(){ +inline void ffw::quaternion::normalize(){ float n = 1.0f/sqrt(x*x+y*y+z*z+w*w); x = x * n; y = y * n; @@ -130,7 +107,7 @@ inline void ffw::uaternion::normalize(){ } ///============================================================================= -inline void ffw::uaternion::getEuler(float* Roll, float* Pitch, float* Yaw) const{ +inline void ffw::quaternion::getEuler(float* Roll, float* Pitch, float* Yaw) const{ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/ float test = x*y + z*w; float heading; diff --git a/third-party-libs/FragmentFramework/include/math/typesOperators.h b/third-party-libs/FragmentFramework/include/math/typesOperators.h index c645d27..988cea5 100644 --- a/third-party-libs/FragmentFramework/include/math/typesOperators.h +++ b/third-party-libs/FragmentFramework/include/math/typesOperators.h @@ -34,13 +34,13 @@ namespace ffw{ @ingroup Math @inline */ - std::ostream& operator << (std::ostream& os, const quaternion& Q); + std::ostream& operator << (std::ostream& os, const color& C); /*! @memberof ffw @ingroup Math @inline */ - template vec4 operator * (mat4& M, const ffw::vec4 &V); + std::ostream& operator << (std::ostream& os, const quaternion& Q); }; #include "typesOperators.inl" diff --git a/third-party-libs/FragmentFramework/include/math/typesOperators.inl b/third-party-libs/FragmentFramework/include/math/typesOperators.inl index 9fea09e..295f88d 100644 --- a/third-party-libs/FragmentFramework/include/math/typesOperators.inl +++ b/third-party-libs/FragmentFramework/include/math/typesOperators.inl @@ -35,16 +35,9 @@ inline std::ostream& ffw::operator << (std::ostream& os, const ffw::quaternion& } ///============================================================================= -template -ffw::vec4 ffw::operator * (ffw::mat4& M, const ffw::vec4 &V){ - ffw::vec4 r; - - r.x = V.x*M[0]; + V.y*M[4]; + V.z*M[8]; + V.w*M[12]; - r.y = V.x*M[1]; + V.y*M[5]; + V.z*M[9]; + V.w*M[13]; - r.z = V.x*M[2]; + V.y*M[6]; + V.z*M[10]; + V.w*M[14]; - r.w = V.x*M[3]; + V.y*M[7]; + V.z*M[11]; + V.w*M[15]; - - return r; +inline std::ostream& ffw::operator << (std::ostream& os, const ffw::color& C){ + os << C.r << ", " << C.g << ", " << C.b << ", " << C.a; + return os; } #endif diff --git a/third-party-libs/FragmentFramework/include/math/vec2.h b/third-party-libs/FragmentFramework/include/math/vec2.h index fe3ced6..1589fa6 100644 --- a/third-party-libs/FragmentFramework/include/math/vec2.h +++ b/third-party-libs/FragmentFramework/include/math/vec2.h @@ -14,7 +14,7 @@ namespace ffw { /*! @memberof ffw @ingroup Math - + @brief 2D vector */ template struct vec2 { @@ -22,14 +22,14 @@ namespace ffw { /*! @memberof vec2 @ingroup Math - + @brief X component */ T x; /*! @memberof vec2 @ingroup Math - + @brief Y component */ T y; @@ -37,9 +37,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Constructor - + @details Sets all components to zero. */ vec2(); @@ -47,9 +47,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Constructor - + @param [in] X Component X @param [in] Y Component Y */ @@ -58,9 +58,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Constructor - + @param [in] Value Value of all components */ vec2(T Value); @@ -68,11 +68,11 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Constructor - + @details Copies value from another vector. - + @param [in] V Another vector */ vec2(const vec2& V); @@ -80,11 +80,11 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Constructor - + @param [in] List Bracket list - + @code ffw::vec2 v{1.0f, 1.5f}; @endcode @@ -94,9 +94,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Sets the vector - + @param [in] X Component X @param [in] Y Component Y */ @@ -105,11 +105,11 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Sets the vector - + @details Sets same value for all component. - + @param [in] Value Value of all components */ void set(T Value); @@ -117,11 +117,11 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Sets the vector - + @details Copies value from another vector. - + @param [in] V Another vector */ void set(const vec2& V); @@ -129,11 +129,11 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Sets the vector - + @param [in] List Bracket list - + @code ffw::vec2 v; v.set({1.0f, 1.5f}); @@ -144,11 +144,11 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Subtraction - + @details Returns negative value of vector if used as unary minus. - + @return Negative vector */ vec2 operator - () const; @@ -156,9 +156,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Assign a value - + @return Reference to object */ vec2& operator = (const vec2& V); @@ -166,9 +166,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Addition - + @return Product of addition */ vec2 operator + (const vec2& V) const; @@ -176,9 +176,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Addition assignment - + @return Reference to object */ vec2& operator += (const vec2& V); @@ -186,9 +186,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Subtraction - + @return Product of substraction */ vec2 operator - (const vec2& V) const; @@ -196,9 +196,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Subtraction assignment - + @return Reference to object */ vec2& operator -= (const vec2& V); @@ -206,9 +206,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Division - + @return Product of division */ vec2 operator / (const vec2& V) const; @@ -216,9 +216,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Division assignment - + @return Reference to object */ vec2& operator /= (const vec2& V); @@ -226,9 +226,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Multiplication - + @return Product of multiplication */ vec2 operator * (const vec2& V) const; @@ -236,9 +236,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Multiplication assignment - + @return Reference to object */ vec2& operator *= (const vec2& V); @@ -246,9 +246,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Assign a value - + @return Reference to object */ vec2& operator = (const T& Val); @@ -256,9 +256,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Addition - + @return Product of addition */ vec2 operator + (const T& Val) const; @@ -266,9 +266,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Addition assignment - + @return Reference to object */ vec2& operator += (const T& Val); @@ -276,9 +276,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Subtraction - + @return Product of substraction */ vec2 operator - (const T& Val) const; @@ -286,9 +286,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Subtraction assignment - + @return Reference to object */ vec2& operator -= (const T& Val); @@ -296,9 +296,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Division - + @return Product of division */ vec2 operator / (const T& Val) const; @@ -306,9 +306,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Division assignment - + @return Reference to object */ vec2& operator /= (const T& Val); @@ -316,9 +316,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Multiplication - + @return Product of multiplication */ vec2 operator * (const T& Val) const; @@ -326,9 +326,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Multiplication assignment - + @return Reference to object */ vec2& operator *= (const T& Val); @@ -336,9 +336,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Checks if two vectors are not equal - + @return True if not equal */ bool operator != (const vec2& V) const; @@ -346,9 +346,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Checks if two vectors are equal - + @return True if equal */ bool operator == (const vec2& V) const; @@ -356,11 +356,11 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Roatates the vector - + @param [in] Deg Degrees - + @return Reference to object */ vec2& rotate(const float Deg); @@ -368,11 +368,11 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Rotates the vector - + @param [in] Rad Radians - + @return Reference to object */ vec2& rotateRad(const float Rad); @@ -380,9 +380,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Normalizes the vector to length of 1.0 - + @return Reference to object */ vec2& normalize(); @@ -390,11 +390,11 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Scales the vector by given value - + @param [in] Val Scale - + @return Reference to object */ vec2& scale(const T Val); @@ -402,9 +402,9 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Returns length of vector - + @return Length of vector */ float length() const; @@ -412,13 +412,37 @@ namespace ffw { @memberof vec2 @ingroup Math @inline - + @brief Returns squared length of vector - + @return Squared length of vector */ T lengthSqrd() const; + /*! + @memberof vec2 + @ingroup Math + @inline + + @brief Explicit operator to cast between different data types + */ + template explicit operator vec2() const; }; + + /*! + @memberof ffw + @ingroup Math + */ + typedef vec2 vec2f; + /*! + @memberof ffw + @ingroup Math + */ + typedef vec2 vec2i; + /*! + @memberof ffw + @ingroup Math + */ + typedef vec2 vec2d; }; #endif diff --git a/third-party-libs/FragmentFramework/include/math/vec2.inl b/third-party-libs/FragmentFramework/include/math/vec2.inl index 7a4c02d..b7704c0 100644 --- a/third-party-libs/FragmentFramework/include/math/vec2.inl +++ b/third-party-libs/FragmentFramework/include/math/vec2.inl @@ -270,4 +270,11 @@ inline T ffw::vec2::lengthSqrd() const{ return (x*x + y*y); } +///============================================================================= +template +template +inline ffw::vec2::operator ffw::vec2() const { + return ffw::vec2((S)x, (S)y); +} + #endif diff --git a/third-party-libs/FragmentFramework/include/math/vec3.h b/third-party-libs/FragmentFramework/include/math/vec3.h index c9087ae..b7dc694 100644 --- a/third-party-libs/FragmentFramework/include/math/vec3.h +++ b/third-party-libs/FragmentFramework/include/math/vec3.h @@ -14,7 +14,7 @@ namespace ffw { /*! @memberof ffw @ingroup Math - + @brief 3D vector */ template struct vec3 { @@ -22,21 +22,21 @@ namespace ffw { /*! @memberof vec3 @ingroup Math - + @brief X component */ T x; /*! @memberof vec3 @ingroup Math - + @brief Y component */ T y; /*! @memberof vec3 @ingroup Math - + @brief Z component */ T z; @@ -44,9 +44,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Constructor - + @details Sets all components to zero. */ vec3(); @@ -54,9 +54,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Constructor - + @param [in] X Component X @param [in] Y Component Y @param [in] Z Component Z @@ -66,9 +66,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Constructor - + @param [in] Value Value of all components */ vec3(T Value); @@ -76,11 +76,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Constructor - + @details Copies value from another vector. - + @param [in] V Another vector */ vec3(const vec3& V); @@ -88,11 +88,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Constructor - + @param [in] List Bracket list - + @code ffw::vec3 v{1.0f, 1.5f, 0.2f}; @endcode @@ -102,9 +102,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Sets the vector - + @param [in] X Component X @param [in] Y Component Y @param [in] Z Component Z @@ -114,11 +114,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Sets the vector - + @details Sets same value for all component. - + @param [in] Value Value of all components */ void set(T Value); @@ -126,11 +126,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Sets the vector - + @details Copies value from another vector. - + @param [in] V Another vector */ void set(const vec3& V); @@ -138,11 +138,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Sets the vector - + @param [in] List Bracket list - + @code ffw::vec3 v; v.set({1.0f, 1.5f, 0.2f}); @@ -153,11 +153,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Subtraction - + @details Returns negative value of vector if used as unary minus. - + @return Negative vector */ vec3 operator - () const; @@ -165,9 +165,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Assign a value - + @return Reference to object */ vec3& operator = (const vec3& V); @@ -175,9 +175,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Addition - + @return Product of addition */ vec3 operator + (const vec3& V) const; @@ -185,9 +185,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Addition assignment - + @return Reference to object */ vec3& operator += (const vec3& V); @@ -195,9 +195,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Subtraction - + @return Product of substraction */ vec3 operator - (const vec3& V) const; @@ -205,9 +205,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Subtraction assignment - + @return Reference to object */ vec3& operator -= (const vec3& V); @@ -215,9 +215,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Division - + @return Product of division */ vec3 operator / (const vec3& V) const; @@ -225,9 +225,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Division assignment - + @return Reference to object */ vec3& operator /= (const vec3& V); @@ -235,9 +235,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Multiplication - + @return Product of multiplication */ vec3 operator * (const vec3& V) const; @@ -245,9 +245,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Multiplication assignment - + @return Reference to object */ vec3& operator *= (const vec3& V); @@ -255,9 +255,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Assign a value - + @return Reference to object */ vec3& operator = (const T& Val); @@ -265,9 +265,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Addition - + @return Product of addition */ vec3 operator + (const T& Val) const; @@ -275,9 +275,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Addition assignment - + @return Reference to object */ vec3& operator += (const T& Val); @@ -285,9 +285,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Subtraction - + @return Product of substraction */ vec3 operator - (const T& Val) const; @@ -295,9 +295,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Subtraction assignment - + @return Reference to object */ vec3& operator -= (const T& Val); @@ -305,9 +305,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Division - + @return Product of division */ vec3 operator / (const T& Val) const; @@ -315,9 +315,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Division assignment - + @return Reference to object */ vec3& operator /= (const T& Val); @@ -325,9 +325,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Multiplication - + @return Product of multiplication */ vec3 operator * (const T& Val) const; @@ -335,9 +335,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Multiplication assignment - + @return Reference to object */ vec3& operator *= (const T& Val); @@ -345,9 +345,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Checks if two vectors are not equal - + @return True if not equal */ bool operator != (const vec3& V) const; @@ -355,9 +355,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Checks if two vectors are equal - + @return True if equal */ bool operator == (const vec3& V) const; @@ -365,12 +365,12 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Roatates the vector by given axis - + @param [in] Deg Degrees @param [in] Axis Axis of rotation - + @return Reference to object */ vec3& rotateByAxis(const float Deg, vec3 Axis); @@ -378,11 +378,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Roatates the vector on X axis - + @param [in] Deg Degrees - + @return Reference to object */ vec3& rotateX(const float Deg); @@ -390,11 +390,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Roatates the vector on Y axis - + @param [in] Deg Degrees - + @return Reference to object */ vec3& rotateY(const float Deg); @@ -402,11 +402,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Roatates the vector on Z axis - + @param [in] Deg Degrees - + @return Reference to object */ vec3& rotateZ(const float Deg); @@ -414,11 +414,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Roatates the vector on X axis - + @param [in] Rad Radians - + @return Reference to object */ vec3& rotateXRad(const float Rad); @@ -426,11 +426,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Roatates the vector on Y axis - + @param [in] Rad Radians - + @return Reference to object */ vec3& rotateYRad(const float Rad); @@ -438,11 +438,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Roatates the vector on Z axis - + @param [in] Rad Radians - + @return Reference to object */ vec3& rotateZRad(const float Rad); @@ -450,9 +450,21 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + + @brief Roatates the vector by quaternion + + @param [in] Q Quaternion + + @return Reference to object + */ + vec3& rotateByQuaternion(const ffw::quaternion& Q); + /*! + @memberof vec3 + @ingroup Math + @inline + @brief Normalizes the vector to length of 1.0 - + @return Reference to object */ vec3& normalize(); @@ -460,11 +472,11 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Scales the vector by given value - + @param [in] Val Scale - + @return Reference to object */ vec3& scale(const T Val); @@ -472,9 +484,9 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Returns length of vector - + @return Length of vector */ float length() const; @@ -482,13 +494,37 @@ namespace ffw { @memberof vec3 @ingroup Math @inline - + @brief Returns squared length of vector - + @return Squared length of vector */ T lengthSqrd() const; + /*! + @memberof vec3 + @ingroup Math + @inline + + @brief Explicit operator to cast between different data types + */ + template explicit operator vec3() const; }; + + /*! + @memberof ffw + @ingroup Math + */ + typedef vec3 vec3f; + /*! + @memberof ffw + @ingroup Math + */ + typedef vec3 vec3i; + /*! + @memberof ffw + @ingroup Math + */ + typedef vec3 vec3d; }; #endif diff --git a/third-party-libs/FragmentFramework/include/math/vec3.inl b/third-party-libs/FragmentFramework/include/math/vec3.inl index e626033..6a97837 100644 --- a/third-party-libs/FragmentFramework/include/math/vec3.inl +++ b/third-party-libs/FragmentFramework/include/math/vec3.inl @@ -324,6 +324,39 @@ inline ffw::vec3& ffw::vec3::rotateZRad(const float Rad){ return *this; } +///============================================================================= +template +inline ffw::vec3& ffw::vec3::rotateByQuaternion(const ffw::quaternion& Q){ + /*float oldX = x; + float oldY = y; + float oldZ = z; + x = (1.0f - 2.0f*Q.y*Q.y - 2.0f*Q.z*Q.z)*oldX + (2.0f*Q.x*Q.y - 2.0f*Q.z*Q.w) *oldY + (2.0f*Q.x*Q.z + 2.0f*Q.y*Q.w) *oldZ; + y = (2.0f*Q.x*Q.y + 2.0f*Q.z*Q.w) *oldX + (1.0f - 2.0f*Q.x*Q.x - 2.0f*Q.z*Q.z)*oldY + (2.0f*Q.y*Q.z - 2.0f*Q.x*Q.w) *oldZ; + y = (2.0f*Q.x*Q.z - 2.0f*Q.y*Q.w) *oldX + (2.0f*Q.y*Q.z + 2.0f*Q.x*Q.w) *oldY + (1.0f - 2.0f*Q.x*Q.x - 2.0f*Q.y*Q.y)*oldZ; + return *this;*/ + + /*ofVec3f uv, uuv; + ofVec3f qvec(_v.x, _v.y, _v.z); + uv = qvec.getCrossed(v); + uuv = qvec.getCrossed(uv); + uv *= (2.0f * _v.w); + uuv *= 2.0f; + return v + uv + uuv;*/ + + ffw::vec3f qvc; + qvc.x = Q.y*z - Q.z*y; + qvc.y = Q.z*x - Q.x*z; + qvc.z = Q.x*y - Q.y*x; + ffw::vec3f qvcc; + qvcc.x = Q.y*qvc.z - Q.z*qvc.y; + qvcc.y = Q.z*qvc.x - Q.x*qvc.z; + qvcc.z = Q.x*qvc.y - Q.y*qvc.x; + qvc *= (2.0f * Q.w); + qvcc *= 2.0f; + *this += qvc + qvcc; + return *this; +} + ///============================================================================= template inline ffw::vec3& ffw::vec3::normalize(){ @@ -357,4 +390,11 @@ inline T ffw::vec3::lengthSqrd() const{ return (x*x + y*y + z*z); } +///============================================================================= +template +template +inline ffw::vec3::operator ffw::vec3() const { + return ffw::vec3((S)x, (S)y, (S)z); +} + #endif diff --git a/third-party-libs/FragmentFramework/include/math/vec4.h b/third-party-libs/FragmentFramework/include/math/vec4.h index 036527f..665e2a0 100644 --- a/third-party-libs/FragmentFramework/include/math/vec4.h +++ b/third-party-libs/FragmentFramework/include/math/vec4.h @@ -14,7 +14,7 @@ namespace ffw { /*! @memberof ffw @ingroup Math - + @brief 4D vector */ template struct vec4 { @@ -22,28 +22,28 @@ namespace ffw { /*! @memberof vec4 @ingroup Math - + @brief X component */ T x; /*! @memberof vec4 @ingroup Math - + @brief Y component */ T y; /*! @memberof vec4 @ingroup Math - + @brief Z component */ T z; /*! @memberof vec4 @ingroup Math - + @brief W component */ T w; @@ -51,9 +51,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Constructor - + @details Sets all components to zero. */ vec4(); @@ -61,9 +61,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Constructor - + @param [in] X Component X @param [in] Y Component Y @param [in] Z Component Z @@ -74,9 +74,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Constructor - + @param [in] Value Value of all components */ vec4(T Value); @@ -84,11 +84,11 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Constructor - + @details Copies value from another vector. - + @param [in] V Another vector */ vec4(const vec4& V); @@ -96,11 +96,11 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Constructor - + @param [in] List Bracket list - + @code ffw::vec4 v{1.0f, 1.5f, 0.2f, -9.0f}; @endcode @@ -110,9 +110,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Sets the vector - + @param [in] X Component X @param [in] Y Component Y @param [in] Z Component Z @@ -123,11 +123,11 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Sets the vector - + @details Sets same value for all component. - + @param [in] Value Value of all components */ void set(T Value); @@ -135,11 +135,11 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Sets the vector - + @details Copies value from another vector. - + @param [in] V Another vector */ void set(const vec4& V); @@ -147,11 +147,11 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Sets the vector - + @param [in] List Bracket list - + @code ffw::vec4 v; v.set({1.0f, 1.5f, 0.2f, -9.0f}); @@ -162,11 +162,11 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Subtraction - + @details Returns negative value of vector if used as unary minus. - + @return Negative vector */ vec4 operator - () const; @@ -174,9 +174,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Assign a value - + @return Reference to object */ vec4& operator = (const vec4& V); @@ -184,9 +184,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Addition - + @return Product of addition */ vec4 operator + (const vec4& V) const; @@ -194,9 +194,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Addition assignment - + @return Reference to object */ vec4& operator += (const vec4& V); @@ -204,9 +204,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Subtraction - + @return Product of substraction */ vec4 operator - (const vec4& V) const; @@ -214,9 +214,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Subtraction assignment - + @return Reference to object */ vec4& operator -= (const vec4& V); @@ -224,9 +224,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Division - + @return Product of division */ vec4 operator / (const vec4& V) const; @@ -234,9 +234,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Division assignment - + @return Reference to object */ vec4& operator /= (const vec4& V); @@ -244,9 +244,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Multiplication - + @return Product of multiplication */ vec4 operator * (const vec4& V) const; @@ -254,9 +254,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Multiplication assignment - + @return Reference to object */ vec4& operator *= (const vec4& V); @@ -264,9 +264,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Assign a value - + @return Reference to object */ vec4& operator = (const T& Val); @@ -274,9 +274,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Addition - + @return Product of addition */ vec4 operator + (const T& Val) const; @@ -284,9 +284,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Addition assignment - + @return Reference to object */ vec4& operator += (const T& Val); @@ -294,9 +294,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Subtraction - + @return Product of substraction */ vec4 operator - (const T& Val) const; @@ -304,9 +304,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Subtraction assignment - + @return Reference to object */ vec4& operator -= (const T& Val); @@ -314,9 +314,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Division - + @return Product of division */ vec4 operator / (const T& Val) const; @@ -324,9 +324,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Division assignment - + @return Reference to object */ vec4& operator /= (const T& Val); @@ -334,9 +334,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Multiplication - + @return Product of multiplication */ vec4 operator * (const T& Val) const; @@ -344,9 +344,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Multiplication assignment - + @return Reference to object */ vec4& operator *= (const T& Val); @@ -354,9 +354,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Checks if two vectors are not equal - + @return True if not equal */ bool operator != (const vec4& V) const; @@ -364,9 +364,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Checks if two vectors are equal - + @return True if equal */ bool operator == (const vec4& V) const; @@ -374,9 +374,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Normalizes the vector to length of 1.0 - + @return Reference to object */ vec4& normalize(); @@ -384,11 +384,11 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Scales the vector by given value - + @param [in] Val Scale - + @return Reference to object */ vec4& scale(const T Val); @@ -396,9 +396,9 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Returns length of vector - + @return Length of vector */ float length() const; @@ -406,13 +406,37 @@ namespace ffw { @memberof vec4 @ingroup Math @inline - + @brief Returns squared length of vector - + @return Squared length of vector */ T lengthSqrd() const; + /*! + @memberof vec4 + @ingroup Math + @inline + + @brief Explicit operator to cast between different data types + */ + template explicit operator vec4() const; }; + + /*! + @memberof ffw + @ingroup Math + */ + typedef vec4 vec4f; + /*! + @memberof ffw + @ingroup Math + */ + typedef vec4 vec4i; + /*! + @memberof ffw + @ingroup Math + */ + typedef vec4 vec4d; }; #endif diff --git a/third-party-libs/FragmentFramework/include/math/vec4.inl b/third-party-libs/FragmentFramework/include/math/vec4.inl index db83cf4..905e81f 100644 --- a/third-party-libs/FragmentFramework/include/math/vec4.inl +++ b/third-party-libs/FragmentFramework/include/math/vec4.inl @@ -7,6 +7,7 @@ #ifndef FFW_VEC4_INL #define FFW_VEC4_INL +///============================================================================= template inline ffw::vec4::vec4(){ x = 0; @@ -15,6 +16,7 @@ inline ffw::vec4::vec4(){ w = 0; } +///============================================================================= template inline ffw::vec4::vec4(T X, T Y, T Z, T W){ x = X; @@ -23,6 +25,7 @@ inline ffw::vec4::vec4(T X, T Y, T Z, T W){ w = W; } +///============================================================================= template inline ffw::vec4::vec4(T Value){ x = Value; @@ -31,6 +34,7 @@ inline ffw::vec4::vec4(T Value){ w = Value; } +///============================================================================= template inline ffw::vec4::vec4(const vec4& V){ x = V.x; @@ -39,6 +43,7 @@ inline ffw::vec4::vec4(const vec4& V){ w = V.w; } +///============================================================================= template inline ffw::vec4::vec4(std::initializer_list List){ if(List.size() != 4){ @@ -54,6 +59,7 @@ inline ffw::vec4::vec4(std::initializer_list List){ w = *(List.begin()+3); } +///============================================================================= template inline void ffw::vec4::set(T X, T Y, T Z, T W){ x = X; @@ -62,6 +68,7 @@ inline void ffw::vec4::set(T X, T Y, T Z, T W){ w = W; } +///============================================================================= template inline void ffw::vec4::set(T Value){ x = Value; @@ -70,6 +77,7 @@ inline void ffw::vec4::set(T Value){ w = Value; } +///============================================================================= template inline void ffw::vec4::set(const vec4& V){ x = V.x; @@ -78,6 +86,7 @@ inline void ffw::vec4::set(const vec4& V){ w = V.w; } +///============================================================================= template inline void ffw::vec4::set(std::initializer_list List){ if(List.size() != 4)return; @@ -87,11 +96,13 @@ inline void ffw::vec4::set(std::initializer_list List){ w = *(List.begin()+2); } +///============================================================================= template inline ffw::vec4 ffw::vec4::operator - () const{ return vec4(-x, -y, -z, -w); } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator = (const vec4& V){ x = V.x; @@ -101,11 +112,13 @@ inline ffw::vec4& ffw::vec4::operator = (const vec4& V){ return *this; } +///============================================================================= template inline ffw::vec4 ffw::vec4::operator + (const vec4& V) const{ return vec4(x + V.x, y + V.y, z + V.z, w + V.w); } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator += (const vec4& V){ x += V.x; @@ -115,11 +128,13 @@ inline ffw::vec4& ffw::vec4::operator += (const vec4& V){ return *this; } +///============================================================================= template inline ffw::vec4 ffw::vec4::operator - (const vec4& V) const{ return vec4(x - V.x, y - V.y, z - V.z, w - V.w); } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator -= (const vec4& V){ x -= V.x; @@ -129,11 +144,13 @@ inline ffw::vec4& ffw::vec4::operator -= (const vec4& V){ return *this; } +///============================================================================= template inline ffw::vec4 ffw::vec4::operator / (const vec4& V) const{ return vec4(x / V.x, y / V.y, z / V.z, w / V.w); } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator /= (const vec4& V){ x /= V.x; @@ -143,11 +160,13 @@ inline ffw::vec4& ffw::vec4::operator /= (const vec4& V){ return *this; } +///============================================================================= template inline ffw::vec4 ffw::vec4::operator * (const vec4& V) const{ return vec4(x * V.x, y * V.y, z * V.z, w * V.w); } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator *= (const vec4& V){ x *= V.x; @@ -157,6 +176,7 @@ inline ffw::vec4& ffw::vec4::operator *= (const vec4& V){ return *this; } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator = (const T& Val){ x = Val; @@ -166,11 +186,13 @@ inline ffw::vec4& ffw::vec4::operator = (const T& Val){ return *this; } +///============================================================================= template inline ffw::vec4 ffw::vec4::operator + (const T& Val) const{ return vec4(x + Val, y + Val, z + Val, w + Val); } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator += (const T& Val){ x += Val; @@ -180,11 +202,13 @@ inline ffw::vec4& ffw::vec4::operator += (const T& Val){ return *this; } +///============================================================================= template inline ffw::vec4 ffw::vec4::operator - (const T& Val) const{ return vec4(x - Val, y - Val, z - Val, w - Val); } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator -= (const T& Val){ x -= Val; @@ -194,11 +218,13 @@ inline ffw::vec4& ffw::vec4::operator -= (const T& Val){ return *this; } +///============================================================================= template inline ffw::vec4 ffw::vec4::operator / (const T& Val) const{ return vec4(x / Val, y / Val, z / Val, w / Val); } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator /= (const T& Val){ x /= Val; @@ -208,11 +234,13 @@ inline ffw::vec4& ffw::vec4::operator /= (const T& Val){ return *this; } +///============================================================================= template inline ffw::vec4 ffw::vec4::operator * (const T& Val) const{ return vec4(x * Val, y * Val, z * Val, w * Val); } +///============================================================================= template inline ffw::vec4& ffw::vec4::operator *= (const T& Val){ x *= Val; @@ -222,16 +250,19 @@ inline ffw::vec4& ffw::vec4::operator *= (const T& Val){ return *this; } +///============================================================================= template inline bool ffw::vec4::operator != (const vec4& V) const{ return (x != V.x || y != V.y || z != V.z || w != V.w); } +///============================================================================= template inline bool ffw::vec4::operator == (const vec4& V) const{ return (x == V.x && y == V.y && z == V.z && w == V.w); } +///============================================================================= template inline ffw::vec4& ffw::vec4::normalize(){ float l = sqrtf(x*x + y*y + z*z + w*w); @@ -244,6 +275,7 @@ inline ffw::vec4& ffw::vec4::normalize(){ return *this; } +///============================================================================= template inline ffw::vec4& ffw::vec4::scale(const T Val){ x = x*Val; @@ -253,15 +285,24 @@ inline ffw::vec4& ffw::vec4::scale(const T Val){ return *this; } +///============================================================================= template inline float ffw::vec4::length() const{ return sqrtf(x*x + y*y + z*z + w*w); } +///============================================================================= template inline T ffw::vec4::lengthSqrd() const{ return (x*x + y*y + z*z + w*w); } +///============================================================================= +template +template +inline ffw::vec4::operator ffw::vec4() const { + return ffw::vec4((S)x, (S)y, (S)z, (S)w); +} + #endif diff --git a/third-party-libs/FragmentFramework/include/systemUtils/listDirectory.h b/third-party-libs/FragmentFramework/include/systemUtils/directory.h similarity index 68% rename from third-party-libs/FragmentFramework/include/systemUtils/listDirectory.h rename to third-party-libs/FragmentFramework/include/systemUtils/directory.h index ce97d84..8066c73 100644 --- a/third-party-libs/FragmentFramework/include/systemUtils/listDirectory.h +++ b/third-party-libs/FragmentFramework/include/systemUtils/directory.h @@ -4,8 +4,8 @@ * Licensed under the MIT License */ -#ifndef FFW_LIST_DIRECTORY -#define FFW_LIST_DIRECTORY +#ifndef FFW_DIR +#define FFW_DIR #include "../dll.h" #include "../math/math.h" @@ -20,28 +20,27 @@ namespace ffw { @memberof ffw @ingroup System */ - class FFW_API listDirectory { + class FFW_API directory { public: - listDirectory(); - ~listDirectory(); + directory(); + ~directory(); /*! - @memberof listDirectory + @memberof directory @ingroup System */ bool open(std::string path); /*! - @memberof listDirectory + @memberof directory @ingroup System */ - const std::vector& getDirectories(); + const std::vector& getDirs(); /*! - @memberof listDirectory + @memberof directory @ingroup System */ const std::vector& getFiles(); private: - bool isFileDirectory(std::string path); std::vector* files; std::vector* dirs; diff --git a/third-party-libs/FragmentFramework/include/systemUtils/win32SysUtils.h b/third-party-libs/FragmentFramework/include/systemUtils/win32SysUtils.h index ccd09d0..7937634 100644 --- a/third-party-libs/FragmentFramework/include/systemUtils/win32SysUtils.h +++ b/third-party-libs/FragmentFramework/include/systemUtils/win32SysUtils.h @@ -18,12 +18,12 @@ namespace ffw { @memberof ffw @ingroup System */ - std::wstring FFW_API openFileDialog(const std::wstring& title); + std::wstring FFW_API openFileDialog(const std::wstring& title, const std::wstring& Path); /*! @memberof ffw @ingroup System */ - std::wstring FFW_API openFolderDialog(const std::wstring& title); + std::wstring FFW_API openFolderDialog(const std::wstring& title, const std::wstring& Path); /*! @memberof ffw @ingroup System diff --git a/third-party-libs/FragmentFramework/include/utilities/loadSaveJson.cpp b/third-party-libs/FragmentFramework/include/utilities/loadSaveJson.cpp index 8f3ef45..cd77030 100644 --- a/third-party-libs/FragmentFramework/include/utilities/loadSaveJson.cpp +++ b/third-party-libs/FragmentFramework/include/utilities/loadSaveJson.cpp @@ -10,7 +10,27 @@ #include "../math/math.h" #include "file.h" -#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, __LINE__, (message) ) +///============================================================================= +static void fixSpaces(const std::string& Str, size_t* Start, size_t* Length){ + size_t begin = *Start; + size_t end = *Start + *Length; + + for(size_t i = begin; i < end; i++){ + if(Str[i] == ' ' || Str[i] == 10 || Str[i] == 13 || Str[i] == 9)continue; + *Start = i; + *Length = end - i; + break; + } + + begin = *Start; + end = *Start + *Length; + + for(size_t i = end-1; i >= begin; i--){ + if(Str[i] == ' ' || Str[i] == 10 || Str[i] == 13 || Str[i] == 9)continue; + *Length = i - *Start +1; + break; + } +} ///============================================================================= static size_t findCollon(const std::string& Str, size_t Start, size_t End){ @@ -201,6 +221,11 @@ static bool parseString(const std::string& Str, size_t Start, size_t End, std::s } } + if(start+1 == end){ + *Out = ""; + return true; + } + if(start -end -1 == 0)return false; Out->assign(Str, start+1, end-start-1); @@ -479,9 +504,11 @@ static void parse(const std::string& Str, size_t Start, size_t End, ffw::var* Ou if(isArray){ bool boolean; if(checkIfInt(Str, pos, pos+length)){ + fixSpaces(Str, &pos, &length); Output->getAsArray().push_back(ffw::stringToVal(Str.substr(pos, pos-length))); } else if(checkIfFloat(Str, pos, pos+length)){ + fixSpaces(Str, &pos, &length); Output->getAsArray().push_back(ffw::stringToVal(Str.substr(pos, pos-length))); } else if(checkIfBool(Str, pos, pos+length, &boolean)){ @@ -501,9 +528,10 @@ static void parse(const std::string& Str, size_t Start, size_t End, ffw::var* Ou for(size_t i = newBegin; i < newEnd; i++){ if(Str[i] == '['){newBegin = i;break;} } - for(size_t i = newEnd; i > newBegin; i--){ + for(size_t i = newEnd-1; i > newBegin; i--){ if(Str[i] == ']'){newEnd = i;break;} } + Output->getAsArray().push_back(ffw::var()); parse(Str, newBegin, newEnd, &Output->getAsArray().at(Output->getAsArray().size()-1)); @@ -513,9 +541,10 @@ static void parse(const std::string& Str, size_t Start, size_t End, ffw::var* Ou for(size_t i = newBegin; i < newEnd; i++){ if(Str[i] == '{'){newBegin = i;break;} } - for(size_t i = newEnd; i > newBegin; i--){ + for(size_t i = newEnd-1; i > newBegin; i--){ if(Str[i] == '}'){newEnd = i;break;} } + Output->getAsArray().push_back(ffw::var()); parse(Str, newBegin, newEnd, &Output->getAsArray().at(Output->getAsArray().size()-1)); @@ -561,7 +590,7 @@ static void parse(const std::string& Str, size_t Start, size_t End, ffw::var* Ou for(size_t i = newBegin; i < newEnd; i++){ if(Str[i] == '['){newBegin = i;break;} } - for(size_t i = newEnd-1; i > newBegin; i--){ + for(size_t i = newEnd-1; i >= newBegin; i--){ if(Str[i] == ']'){newEnd = i;break;} } diff --git a/third-party-libs/FragmentFramework/include/utilities/serialization.cpp b/third-party-libs/FragmentFramework/include/utilities/serialization.cpp index ebf3e08..4423818 100644 --- a/third-party-libs/FragmentFramework/include/utilities/serialization.cpp +++ b/third-party-libs/FragmentFramework/include/utilities/serialization.cpp @@ -9,6 +9,42 @@ #include "loadSaveJson.h" #include "loadSaveTxt.h" +///============================================================================= +template +static void serializeVec2(ffw::var* Data, const ffw::serialization::item& Item){ + ffw::varArray dataArray; + + dataArray.push_back(static_cast*>(Item.ptr)->x); + dataArray.push_back(static_cast*>(Item.ptr)->y); + + Data->getAsObject().insert(std::make_pair(Item.name, dataArray)); +} + +///============================================================================= +template +static void serializeVec3(ffw::var* Data, const ffw::serialization::item& Item){ + ffw::varArray dataArray; + + dataArray.push_back(static_cast*>(Item.ptr)->x); + dataArray.push_back(static_cast*>(Item.ptr)->y); + dataArray.push_back(static_cast*>(Item.ptr)->z); + + Data->getAsObject().insert(std::make_pair(Item.name, dataArray)); +} + +///============================================================================= +template +static void serializeVec4(ffw::var* Data, const ffw::serialization::item& Item){ + ffw::varArray dataArray; + + dataArray.push_back(static_cast*>(Item.ptr)->x); + dataArray.push_back(static_cast*>(Item.ptr)->y); + dataArray.push_back(static_cast*>(Item.ptr)->z); + dataArray.push_back(static_cast*>(Item.ptr)->w); + + Data->getAsObject().insert(std::make_pair(Item.name, dataArray)); +} + ///============================================================================= bool ffw::serialization::serializeAsJson(const std::string& Path, bool Formated){ return serializeAsJson(utf8ToWstr(Path), Formated); @@ -17,11 +53,10 @@ bool ffw::serialization::serializeAsJson(const std::string& Path, bool Formated) ///============================================================================= bool ffw::serialization::serializeAsJson(const std::wstring& Path, bool Formated){ ffw::var data = ffw::varObject(); - // Test - if(!saveTXT(Path, ""))return false; + // Test first + if(!saveTXT(Path, "..."))return false; serializeAsVar(&data, Formated); - return saveJSON(Path, &data, Formated, true); } @@ -30,21 +65,21 @@ bool ffw::serialization::serializeAsVar(ffw::var* Data, bool Formated){ for(const auto& item : varList){ if (item.code == typeid(int).hash_code()){ - Data->getAsObject().insert(std::make_pair(item.name, *(int*)(item.ptr))); + Data->getAsObject().insert(std::make_pair(item.name, *static_cast(item.ptr))); } else if(item.code == typeid(float).hash_code()){ - Data->getAsObject().insert(std::make_pair(item.name, *(float*)(item.ptr))); + Data->getAsObject().insert(std::make_pair(item.name, *static_cast(item.ptr))); } else if(item.code == typeid(bool).hash_code()){ - Data->getAsObject().insert(std::make_pair(item.name, *(bool*)(item.ptr))); + Data->getAsObject().insert(std::make_pair(item.name, *static_cast(item.ptr))); } else if(item.code == typeid(std::string).hash_code()){ - Data->getAsObject().insert(std::make_pair(item.name, *(std::string*)(item.ptr))); + Data->getAsObject().insert(std::make_pair(item.name, *static_cast(item.ptr))); } else if(item.code == typeid(std::vector).hash_code()){ ffw::varArray dataArray; - for(const auto& element : *(std::vector*)(item.ptr)){ + for(const auto& element : *static_cast*>(item.ptr)){ dataArray.push_back(element); } @@ -53,7 +88,7 @@ bool ffw::serialization::serializeAsVar(ffw::var* Data, bool Formated){ } else if(item.code == typeid(std::vector).hash_code()){ ffw::varArray dataArray; - for(const auto& element : *(std::vector*)(item.ptr)){ + for(const auto& element : *static_cast*>(item.ptr)){ dataArray.push_back(element); } @@ -62,7 +97,7 @@ bool ffw::serialization::serializeAsVar(ffw::var* Data, bool Formated){ } else if(item.code == typeid(std::vector).hash_code()){ ffw::varArray dataArray; - for(const bool& element : *(std::vector*)(item.ptr)){ + for(const bool& element : *static_cast*>(item.ptr)){ dataArray.push_back(element); } @@ -71,12 +106,155 @@ bool ffw::serialization::serializeAsVar(ffw::var* Data, bool Formated){ } else if(item.code == typeid(std::vector).hash_code()){ ffw::varArray dataArray; - for(const auto& element : *(std::vector*)(item.ptr)){ + for(const auto& element : *static_cast*>(item.ptr)){ dataArray.push_back(element); } Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + } else if(item.code == typeid(ffw::vec2f).hash_code()){ + serializeVec2(Data, item); + + } else if(item.code == typeid(ffw::vec3f).hash_code()){ + serializeVec3(Data, item); + + } else if(item.code == typeid(ffw::vec4f).hash_code()){ + serializeVec4(Data, item); + + } else if(item.code == typeid(ffw::vec2i).hash_code()){ + serializeVec2(Data, item); + + } else if(item.code == typeid(ffw::vec3i).hash_code()){ + serializeVec3(Data, item); + + } else if(item.code == typeid(ffw::vec4i).hash_code()){ + serializeVec4(Data, item); + + } else if(item.code == typeid(ffw::color).hash_code()){ + ffw::varArray dataArray; + + dataArray.push_back(static_cast(item.ptr)->r); + dataArray.push_back(static_cast(item.ptr)->g); + dataArray.push_back(static_cast(item.ptr)->b); + dataArray.push_back(static_cast(item.ptr)->a); + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + + } else if(item.code == typeid(ffw::quaternion).hash_code()){ + ffw::varArray dataArray; + + dataArray.push_back(static_cast(item.ptr)->x); + dataArray.push_back(static_cast(item.ptr)->y); + dataArray.push_back(static_cast(item.ptr)->z); + dataArray.push_back(static_cast(item.ptr)->w); + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray dataArray; + + for(const auto& element : *static_cast*>(item.ptr)){ + dataArray.push_back(ffw::varArray()); + auto& subDataArray = dataArray[dataArray.size()-1].getAsArray(); + subDataArray.push_back(element.x); + subDataArray.push_back(element.y); + } + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray dataArray; + + for(const auto& element : *static_cast*>(item.ptr)){ + dataArray.push_back(ffw::varArray()); + auto& subDataArray = dataArray[dataArray.size()-1].getAsArray(); + subDataArray.push_back(element.x); + subDataArray.push_back(element.y); + subDataArray.push_back(element.z); + } + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray dataArray; + + for(const auto& element : *static_cast*>(item.ptr)){ + dataArray.push_back(ffw::varArray()); + auto& subDataArray = dataArray[dataArray.size()-1].getAsArray(); + subDataArray.push_back(element.x); + subDataArray.push_back(element.y); + subDataArray.push_back(element.z); + subDataArray.push_back(element.w); + } + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray dataArray; + + for(const auto& element : *static_cast*>(item.ptr)){ + dataArray.push_back(ffw::varArray()); + auto& subDataArray = dataArray[dataArray.size()-1].getAsArray(); + subDataArray.push_back(element.x); + subDataArray.push_back(element.y); + } + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray dataArray; + + for(const auto& element : *static_cast*>(item.ptr)){ + dataArray.push_back(ffw::varArray()); + auto& subDataArray = dataArray[dataArray.size()-1].getAsArray(); + subDataArray.push_back(element.x); + subDataArray.push_back(element.y); + subDataArray.push_back(element.z); + } + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray dataArray; + + for(const auto& element : *static_cast*>(item.ptr)){ + dataArray.push_back(ffw::varArray()); + auto& subDataArray = dataArray[dataArray.size()-1].getAsArray(); + subDataArray.push_back(element.x); + subDataArray.push_back(element.y); + subDataArray.push_back(element.z); + subDataArray.push_back(element.w); + } + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray dataArray; + + for(const auto& element : *static_cast*>(item.ptr)){ + dataArray.push_back(ffw::varArray()); + auto& subDataArray = dataArray[dataArray.size()-1].getAsArray(); + subDataArray.push_back(element.r); + subDataArray.push_back(element.g); + subDataArray.push_back(element.b); + subDataArray.push_back(element.a); + } + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray dataArray; + + for(const auto& element : *static_cast*>(item.ptr)){ + dataArray.push_back(ffw::varArray()); + auto& subDataArray = dataArray[dataArray.size()-1].getAsArray(); + subDataArray.push_back(element.x); + subDataArray.push_back(element.y); + subDataArray.push_back(element.z); + subDataArray.push_back(element.w); + } + + Data->getAsObject().insert(std::make_pair(item.name, dataArray)); } } return true; @@ -110,45 +288,187 @@ bool ffw::serialization::deserializeAsVar(ffw::var* Data, std::vectorgetAsObject().end()){ if (item.code == typeid(int).hash_code()){ - *(int*)(item.ptr) = found->second.getAsInt(); + *static_cast(item.ptr) = found->second.getAsInt(); } else if(item.code == typeid(float).hash_code()){ - *(float*)(item.ptr) = found->second.getAsFloat(); + *static_cast(item.ptr) = found->second.getAsFloat(); } else if(item.code == typeid(bool).hash_code()){ - *(bool*)(item.ptr) = found->second.getAsBool(); + *static_cast(item.ptr) = found->second.getAsBool(); } else if(item.code == typeid(std::string).hash_code()){ - *(std::string*)(item.ptr) = found->second.getAsString(); + *static_cast(item.ptr) = found->second.getAsString(); } else if(item.code == typeid(std::vector).hash_code()){ ffw::varArray& elements = found->second.getAsArray(); for(auto& element : elements){ - ((std::vector*)(item.ptr))->push_back(element.getAsInt()); + static_cast*>(item.ptr)->push_back(element.getAsInt()); } } else if(item.code == typeid(std::vector).hash_code()){ ffw::varArray& elements = found->second.getAsArray(); for(auto& element : elements){ - ((std::vector*)(item.ptr))->push_back(element.getAsFloat()); + static_cast*>(item.ptr)->push_back(element.getAsFloat()); } } else if(item.code == typeid(std::vector).hash_code()){ ffw::varArray& elements = found->second.getAsArray(); for(auto& element : elements){ - ((std::vector*)(item.ptr))->push_back(element.getAsBool()); + static_cast*>(item.ptr)->push_back(element.getAsBool()); } } else if(item.code == typeid(std::vector).hash_code()){ ffw::varArray& elements = found->second.getAsArray(); for(auto& element : elements){ - ((std::vector*)(item.ptr))->push_back(element.getAsString()); + static_cast*>(item.ptr)->push_back(element.getAsString()); } + + } else if(item.code == typeid(ffw::vec2f).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + if(elements.size() != 2){Missing->push_back(item.name); result = false; continue;} + + static_cast(item.ptr)->x = elements[0].getAsFloat(); + static_cast(item.ptr)->y = elements[1].getAsFloat(); + + } else if(item.code == typeid(ffw::vec3f).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + if(elements.size() != 3){Missing->push_back(item.name); result = false; continue;} + + static_cast(item.ptr)->x = elements[0].getAsFloat(); + static_cast(item.ptr)->y = elements[1].getAsFloat(); + static_cast(item.ptr)->z = elements[2].getAsFloat(); + + } else if(item.code == typeid(ffw::vec4f).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + if(elements.size() != 4){Missing->push_back(item.name); result = false; continue;} + + static_cast(item.ptr)->x = elements[0].getAsFloat(); + static_cast(item.ptr)->y = elements[1].getAsFloat(); + static_cast(item.ptr)->z = elements[2].getAsFloat(); + static_cast(item.ptr)->w = elements[3].getAsFloat(); + + } else if(item.code == typeid(ffw::vec2i).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + if(elements.size() != 2){Missing->push_back(item.name); result = false; continue;} + + static_cast(item.ptr)->x = elements[0].getAsInt(); + static_cast(item.ptr)->y = elements[1].getAsInt(); + + } else if(item.code == typeid(ffw::vec3i).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + if(elements.size() != 3){Missing->push_back(item.name); result = false; continue;} + + static_cast(item.ptr)->x = elements[0].getAsInt(); + static_cast(item.ptr)->y = elements[1].getAsInt(); + static_cast(item.ptr)->z = elements[2].getAsInt(); + + } else if(item.code == typeid(ffw::vec4i).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + if(elements.size() != 4){Missing->push_back(item.name); result = false; continue;} + + static_cast(item.ptr)->x = elements[0].getAsInt(); + static_cast(item.ptr)->y = elements[1].getAsInt(); + static_cast(item.ptr)->z = elements[2].getAsInt(); + static_cast(item.ptr)->w = elements[3].getAsInt(); + + } else if(item.code == typeid(ffw::color).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + if(elements.size() != 4){Missing->push_back(item.name); result = false; continue;} + + static_cast(item.ptr)->r = elements[0].getAsFloat(); + static_cast(item.ptr)->g = elements[1].getAsFloat(); + static_cast(item.ptr)->b = elements[2].getAsFloat(); + static_cast(item.ptr)->a = elements[3].getAsFloat(); + + } else if(item.code == typeid(ffw::quaternion).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + if(elements.size() != 4){Missing->push_back(item.name); result = false; continue;} + + static_cast(item.ptr)->x = elements[0].getAsFloat(); + static_cast(item.ptr)->y = elements[1].getAsFloat(); + static_cast(item.ptr)->z = elements[2].getAsFloat(); + static_cast(item.ptr)->w = elements[3].getAsFloat(); + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + + for(auto& element : elements){ + auto& subElements = element.getAsArray(); + if(subElements.size() != 2)continue; + static_cast*>(item.ptr)->push_back(ffw::vec2f(subElements[0].getAsFloat(), subElements[1].getAsFloat())); + } + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + + for(auto& element : elements){ + auto& subElements = element.getAsArray(); + if(subElements.size() != 3)continue; + static_cast*>(item.ptr)->push_back(ffw::vec3f(subElements[0].getAsFloat(), subElements[1].getAsFloat(), subElements[2].getAsFloat())); + } + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + + for(auto& element : elements){ + auto& subElements = element.getAsArray(); + if(subElements.size() != 4)continue; + static_cast*>(item.ptr)->push_back(ffw::vec4f(subElements[0].getAsFloat(), subElements[1].getAsFloat(), subElements[2].getAsFloat(), subElements[3].getAsFloat())); + } + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + + for(auto& element : elements){ + auto& subElements = element.getAsArray(); + if(subElements.size() != 2)continue; + static_cast*>(item.ptr)->push_back(ffw::vec2i(subElements[0].getAsInt(), subElements[1].getAsInt())); + } + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + + for(auto& element : elements){ + auto& subElements = element.getAsArray(); + if(subElements.size() != 3)continue; + static_cast*>(item.ptr)->push_back(ffw::vec3i(subElements[0].getAsInt(), subElements[1].getAsInt(), subElements[2].getAsInt())); + } + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + + for(auto& element : elements){ + auto& subElements = element.getAsArray(); + if(subElements.size() != 4)continue; + static_cast*>(item.ptr)->push_back(ffw::vec4i(subElements[0].getAsInt(), subElements[1].getAsInt(), subElements[2].getAsInt(), subElements[3].getAsInt())); + } + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + + for(auto& element : elements){ + auto& subElements = element.getAsArray(); + + if(subElements.size() != 4)continue; + static_cast*>(item.ptr)->push_back(ffw::color(subElements[0].getAsFloat(), subElements[1].getAsFloat(), subElements[2].getAsFloat(), subElements[3].getAsFloat())); + } + + + } else if(item.code == typeid(std::vector).hash_code()){ + ffw::varArray& elements = found->second.getAsArray(); + + for(auto& element : elements){ + auto& subElements = element.getAsArray(); + if(subElements.size() != 4)continue; + static_cast*>(item.ptr)->push_back(ffw::quaternion(subElements[0].getAsFloat(), subElements[1].getAsFloat(), subElements[2].getAsFloat(), subElements[3].getAsFloat())); + } + } + } else { if(Missing != NULL) Missing->push_back(item.name); result = false; diff --git a/third-party-libs/FragmentFramework/include/utilities/serialization.h b/third-party-libs/FragmentFramework/include/utilities/serialization.h index 133a4be..3510581 100644 --- a/third-party-libs/FragmentFramework/include/utilities/serialization.h +++ b/third-party-libs/FragmentFramework/include/utilities/serialization.h @@ -54,7 +54,6 @@ namespace ffw{ */ bool deserializeAsJson(const std::wstring& Path, std::vector* Missing); - private: struct item { item(void* Ptr, const std::string& Name, size_t Code):ptr(Ptr),name(Name),code(Code){} void* ptr; @@ -62,6 +61,7 @@ namespace ffw{ size_t code; }; + private: std::vector varList; protected: diff --git a/third-party-libs/FragmentFramework/lib/FragmentFramework.dll b/third-party-libs/FragmentFramework/lib/mingw/FragmentFramework.dll similarity index 75% rename from third-party-libs/FragmentFramework/lib/FragmentFramework.dll rename to third-party-libs/FragmentFramework/lib/mingw/FragmentFramework.dll index 8f062dc635f8af32a620a81c05875270dfb7f50a..e11d8c778d92393e6f7b1ca7176df979f3479919 100644 GIT binary patch delta 391082 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 l8^-2X3mU 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 $TGE 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 XWhIawRPX=7QM$g{jlC}4%oY28<~fPg delta 361095 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ⅅ2ipErCH&Fo|e^qL)HiNQPrNOG! z7h|LpA*?q$5hFDwcr`}K74mx7(kDxvb%ea`wu3XJb&4Tu`%YWvV(CF$a~xEp=3xT~ zyMl`lE-@v6)_sN42s09l{~9Fa5x-0Ji zMt=zXtyf7i_4RTQFLBEQdGl)Nx?2%NQCb>m$r`lpv*68IBjKpuV$zHbsDxzX7oWR% zUwu}n8?3|D6{u|LMyX4~Qjl*_Z#B`^3r)g=I6DABV>SJSn`Iz)pqhe3OZ8}@G^q&@ z(b_H|#YEq6waKq-lDfE&Q2Y6tx4Af=Bvl+7SEamslhi>{m7}A?h)ij)KOs%3N{me} zpllmAOYQY4qy5~?@5l&ZJf9&=u1XD|8f*70(j0v~2Xq&kD!}y7R%wFaDFSXx%2xxI znbfnX%MKX#%2TvOnHs!zW9t1%+<<27w@C>G-Hx5MJ=>%UdIN;m;@*)u8r4wNF3G#D zn-8T%*05;`z6<-MQG&q_Nn2%0y$yi~Ov^@BOqgxi(lx@^5)MdKgCPhg!w0Y^1cAvP zU>xZN+JO(Gfo^K(mt)eGia`d>c2UAN%ZPgBE1KwqAwkpsN_e~wx~)A<0N7^zS9(AT zBHNftQg1y?N!iyD-Xj3!v&)j*Ab5DOjo(T$TYGx#inWf;wntpF7>`l?wogj3o4kN6 zDYH*X!Cs>mYgR1X6^XI94H1gn{JfZ(U3wsZ_J>{S=~6|rgE-6aT`#ssFSqw{ z5Dj(l0pQJ3Xws2YoE2W|3%%S=aCk9;Ah!-H1qYSY7hTbZ;?6U8eeHSZ65Q@`%08md z?PZJ5a_(2nwTA-g3D=p=t4WVXa3UUFCANK4i@NbVuz zwq&D&VWlAqNR@><#+MaX3?LRedN zbEu4Mv9_%FF!?s(Hhr}G3Km9fZIg$~@8JbPJ*z(oY4N?v?xW-^BHS1y;|pF0q>Yug z7!uD8*&CalUfITl!}KUEeu%?uRdG(g$m1Ct?&J6z4)Zw7<Y_pUL z^5oE`N-&4MJi!_s&*g9(hj|=kbC}8bvpFo_(3jKwI1E=|Vr82kjtJ!NEQdQdJi=i& zF369=D2^Y;;S~;}IrQMLh{H?{b2#ilauVVCC@wIN!{Z!=amG;`cH=OzN&tr;9ES6B zt{i%Bc$d><9(VBgM8HHUdoPYC<*=AT9Z%rNVF|}i<1mTCQJfyb|?shK10 zs=9&Wg9m|JsE@%X>R6{3xud)kpT^3Q)|Vr>)$Fx2i@gyezaRkakC9skf=WG;7g585 zjA}+w=<9V4Tw%<7YeZ@C)35|3+2n~7kC7sgrwBs|(qqLiM=zO?2a?o;IHD)vaHaX_ z030`D^o@s&#OK(F#bNOEvS6$-vtS0C3w2O!9pD1Cuqw6%V1qy-a6Z5}W+1K|zm(-c zS@Fz^ubZzSMq9pDU#g4318K1C!^v_BvJQsn{0q{;eOn}#R_rBJx%*Og#db(q3U58F zE3MxShUBLWr1hgw5Ltw#hYZqy%}U{sBt)z|*C6)!Izm-SDILK4IWb%NxzJ-Askm>V zOU`#)@drttKhWC1+RwV@ZL(=^vCMgH`H`0Bil1YZsSKOEUR#bEOy@4 zS@10Gf2QUox|Qc7=j&hcCd9J+a%v5>20SejSsdCgvh}J)}_R&1w$Hf}v-OpK{tCsdT5;rB$cd|)Kh)qTWIZ8Z^s+Dv&y zI{vDPJuoY%$;#Nmh+7-qX!1pf z?AiMihV2bR%o;&{Bt!N-08X{c5>b&A0x6b=$Cc)ZYSSRcK7(T`%}cd-rP)Ev1TH7Z zs{UCj#l^As5*~%o{+KvJmKs7$rMVdF)}BeEvg^|UENF(@Scs2g182y-bybFtNESas z_7f7b*sd9JmkvN$4Aux&OF!hDQfEuMcKXUfgZJQej8#&Wy0ughNI`)H=@Hw`3o1Jl&5?p%IWiWZ-u2GDvEeke2% zjyzVa{|c-lN#{9xd_#?iT+IdHHf5EyfswYCX2~-RP3IsP4Q25h&f+j`06YG=d{=lp zz_w?Oj4!)B9bhYaQ#J^CPxfP++(Iz=*~GarrtrRgtiwFa?0o##w0UyA;OA%apHEZ3 z5I;60Ue0j~^4k?#WB*;NRH$OHbLHyrhMRItfX+YvMHasGbQV3}?l6&cL7x}T)i{-Y0E|Luf3|mUdkgT!= z`sW_Lj{Gmst?@x1C6(NbzQf%|yYDaiqaVT-sNqylbd~1F!R+J3STIcUW0w}o4W&hp zR`wFQY_#Lgfqb-BZX?o&gjDKXHlx3-?OXDsIzk_RTWXR#TM$P1+a9izhY7}g?QyJp z^lsT~8=orgY$gowXHPRREgtM|yOSkvts@Ncw=Kz*BlS4=+;;kaJWL-D6o4+eAS$08 z-@>XR$kE$M+o6PZcA#GX>;1kQ*e--8P?;aBCFVetxfQZg&AvsX!5x|lU$vuT`0qoQv+25nbb7DcMJS4wD9It*L z?-eEnFyn`ErC*09*H={Ika_RdDUv zF-;W=dq2V|1Ce_l$zwg@o@1NC*x}$R$PTbQEX0%W!rB1a@(VKVaV-t7{qU9iXC!6J(yb!k-J2IgJ4F-tN64{~`VuDz~4fr4|h`n}GzD9(I zTkxK1Tt}=Ual5~`&9siT^|$4@f)LZuroSuW6XDZ3GUGkDoiM8->u^v0N|@Nu_UxW~ zRgcuTm4rI&6U3gC$vXsH5PRpo{9%KzPP=0LPnNiT1%{F3Aoz5^EhV{;(7J=I-UHd+ z*eo+EDfaptU!(o|Tc!5AUGuz)zrzamc8Pa(mTgFd{DNLs*O8?@gy~5g+3|<+dLg4D zi~3m(a!ZGyW%tx}yQU+v{45_7f+AVhM_6NpN3w)R@=k&tkLA~u9jO1-ZjLh%czW~K zV~5b?r9Z~{5AgJ3EVy<=+J1Q~yXeg-A0A+~206mR>1pm``x3Kk?!U?t4f-Z5_BXk; zu&tvl<2U)L!L8`SBwDq@(&7)3*up>MZNk}(O!-UxK{(x!{q`3s%*l={xKi#cCHH#6Hj~o$GnQuO%U0b?d|&dS#5@(usu$O6!KMK$mysqcZlMI=FPQEfACi`bHk0 z(1z^_Jl2l;;|AQuY~$I92}Wfs72X7+(q8cFWLs@i-p5_$PPUM`$})W5rIYQG21;jr zvu;R^ZfFiQ5j^~Wvv|XOUpazP(_A}kbyJ24UY%^wZpsD0lPmyZG&&%9X~0cJyx8Q? z$<{?y9tre0V!wt;7(C8qH&h0b=WPv@&kTtcJduur2@>$ZvX05W>l3`K-qTQMyj50S zwwhgt<$)2z_%dQ0ls5h8gjXiztK-aS6Ol1~4Yzs+YUzA{sJyI=mfdQFbIxI2Q_&v} zFQ{o~uAgS?v>?rRno_6(I*p)6$Q58EplO#OEaFJ^~6bs!E$ZI{Zfp?Cx18SPp$YJ1A}hiJ88!Jy*^FY3r&;&g@n^I4ZDXj=L@mF3TINV=p255WJUy0Ha;&^-1P(Vj}R2#sq0ZJ}0{4YQWCk9*B z4$7;BrdaFgq?EF!bpH)gbLq2&(r4$Bj%Fgohe66SN_i+)*+vQacUA@)5;p|xiakb8 zZsrEzlD;M#toWFLp6=(K)TiVn{CIQ?u{0%?$+-Lz$BJr>KL5;tiLv%q*(PJ4Hu>2H zC#R=lOCy>i&Z>yYHg~HSILzd*ghS&XO~4Kw_u=s%9uMR25SRCq$90_VERP={NLw1y zI3kL}aU4G5(7^?*;qgNp`f*sn>Dg8M9A4z`F^9z*=5pu-gVZezPi|QSCp_S=h{J6h z`f^yn;Y1FXa(IQq9vp^nIEuqk4qZ6(9LTpc;y9ukhsQb0<~cL+_+1_k=W!2icqzyG zaC{Mud+@l-VIYUD9A1e`)XH)jPw<$-0uGOJn9Jc24xe)PfWsUvFo(xuczhOzGB(_)Sp^E2(=aQawN*aWult@kEi{9~E zWhqTOv-}XQEUi8bDvM~9rM$_dr)Nz=kD3yyj7==AZCh%dB|mMt-NYEMJ!+Mh1}R&U3pS$!sT2Uz$rzIIV49l2eqC4D%&J+ zH!tEak3%mGGkE!=bGVJe9UNwIxQ4@ZDy%B580Q~rU*8Z;GVwh(_YF(p{<@p_Qo(F=-Hg!7(Pxg_Hl0v zmmNac?cPe8CJVOcbYs&7`#E~znG9<`PwS8)sCJH5x3CsrN=w1@MbBN-2$~bv* zQ+m17Gi|6$Ec-Lq;=W3d$4J61_4Tk0@Jt(~QYL3GrJrJEU-wl4JSy=FB*YDbRRz|IeVWP_TvmSp!0n&eiIkbKzVZ<0^0 z*QoO&6@MYdgYAk`yoBx^?0BTo{N<+LCf|NR+jH=3#DB@PMviUIU8`Y#YN+vEapEoJ zSP9dV8VRQwvXFtO7lmnT>_8<@c&#Bz9jF8d&w|+Dfr{VQ&Ks~>2)zRKz$|CVjrd6x zyW@|Jn2_O|tC4sv$BHd0J6Y2Rdy(KJ{7b-F@~zmOsm8+iIi z%Z0RoGJ3qiJ{2ne1@I*oxFE@dvM)!mkA^63w>%pLL9Zrg0-lJ~0t)p+DT_#8uMJfa zg(#8TAF9mhvK}0kLTkSe>qt7d?_{Zf2UOHOPRIKW%C#|VB;LubF?_VhEW?x+g>`Q1 z)F@?8lRmTu9N!Gd@f}hNRcHS>O}ubDTwKKpU3 z(!%p!*!|hQXmp&5>$B-2VO>-`c7G%}4-bC*oAd7clXau(ap$?PQ!ww}yy5S{ori-u zRp-@ZZ$$qeoQKoQROi)Y-;XBe)nT*8kn?by=)ag3aQmOk`=(B{^H8)+UHblW*&Z-) z=b5y`?a1oU3*8mG}<0dEuhg zKGc_bH-0SL5q0FQONGO6_rBQnJ~ z(!sB_8hTLNnNjqr#VDI>3tm>b>pjMxdC@Bc*xHZB!Vy{yE@9ylm4(LJdK?E^WHK}| zI?m`SnSG+tUpTRpwVb5*ytD+Xcxyi|>yRsyV&_{FlSiN>q1SN?)m2sww>C(N_yNS^ zGFL}WAfl;r+J|G{*qm3CL9H8oUs)M?BjIQ3n|?SO#DxyedxRrJYr1mNTg?6n#>7tu z>og{wT&(rhU0zi>dQ8yB7!$qHhIx=NuPtT?uPXgV{D#9wssE(c6sRxcW8!xlJ>D68 zK|`0nU5nMG;V{)u@5!XIWid)ec`lY>7~Rtbs{{1rMeOq!lAffM7 z7BXEKB|P|st(~q!2&aE%m!>O|gyp|u_g)DXbl5)v>>Pf+tH*1s*e#|D!Q922&9EM1bEQCH}39-s# z-ygntUg4>V+6u4CX1~WmVKQNBE8H|&lQ$v`3fmI(ZwfnehQnYG&is!#;*^+n@mHYm z3EJ-xKWxRgpyfM;Q+F&4_Yob`?>Co`uS3){m^$e8ubK zLHJw)wf7I5$}5CpXXD0XO({0#*a9bB7mgLHW^Zbl{``O)nGdTT&tTW)DOxvt+!yWGN;rA^{JWHC|~dHNI3?`O;3xa>7rJ z`Vbti4D?aG@|!x{jz=PRC2g=PIrIy}tP$H-pACwK_jo+howlrq@UzOboc0Gw^*}ir znxHhQ8wtE)WI201L1|yV6C%`X8<(@)APRMhm@NU5`-*8Wcd%-154CgNeP5ME-ShGL z?DqtvRbY~aQg^p8?R0Z6AV^r@@2W+b9muPr|9uv@0OLaQ`z(C{dbks1?DhhsanlT( zUrdED5Uwl>$EEya^70TN3q|U_Q0ZScek!cMDQj8#3})Qf!fMJql^tD(iT&+q?8ZW6 zijX#i^;v{fS@0CLa}icyoA0smMapDB7r}-u#-b&BGF!VCbVm&PVX@NLa3n^@PPP)7 zu#mT~g@?7Yd`d6_oE1-M*Q)mYgMbqMYwqXhS@Edp8 z$t9@yw_@0>CCb>4t*`L4;Up}4Yu1K|Ku4oA(jvZqCK@U`-2JO^1eHa`V>Wv!mIwN~ zEN!W>xbg}UWNhl4?K+zvTsd?Qg4#B z#=N#nX+IzvWb-?72Vm5X)a!qA~c zu&gAd=~jy(HClk&LyC3TA?uKJFhCVCflW-r=+a)!FZ$=3wM84pYh%mJ z6-u*)pAczTPK~2Zjnn+tY9$ttQ&j4q8tPn5oxf6PC%E5aJ60;Qg#|Zge3>k)3uCV* zE8BfPJNdi`n@Vb%ux|`&pMrjRBw=f7pE5?%zA^>c1(mw3M!UkPms8M&Y}eV76lIw2 zg1qOojksQ0+qBVab}Ft4>WqnvnWaqI>s?1*AEY7Klh zW&~TbMwwM7`)_sk11(ohb@#wQis9^Jn zRNLt*qcyiqSqrDPQmM5nSa3A8QxBFnS8)~YeZ#(7tBm!1_t^6)SK=L;8kHHtwB|C} zO4HMCSb|mgxY5?5&nKIAg#~6PzD(Y%G}T{WuV-LiV9z?mPx$UKPGd#n(K`5tU1p8b zP`Ae9J};s3<=TqFhiG{nnxS~Pe@$7+#IVIN)6cG_|mWm)NcK zO0O1bHKkp!F{(&Z(nFZ0@af?@U$b6JY0@JI+cRkqs?^3#SCIojiHI*pqv zZ)5z-FK=Xx$N@SzPo229U1A$CvIpENQcWYiDDZ&??WGRRuKf+|r*>EQ*C1B3LFwT6 zo|d4j8L6(GuWl@2=5&J?dnH{Fm6$js@xYIzFCbo$Gi%j&-i%9XQsxjyJx*Hf+Jf z?aTA*+b!7RnQ@+ZZdE4rD?qnb)*bhBXQbB@Leuk_5kqkbOAXe@v2QtHe>%r zLqF!&s=Qd!h(@XG&W&i>d1^$yE&86*6pX;$TiI_kt;G&zRoxlVkF8)z)2YGzp0ANN z@CIRZjeLp-=F3zmi*Rd&SIPSyR8`4R(97P2bf;!`5LohXFlwXPI#|PP%0wD>&?vs~ zU2y36YA~X(b~TvRN2>eXC!gi=!sZwjH+^A9;vr(X$gFr3& z40|y(#cDpyHg3lXx3b`GHBW8GWd-cScE!8nc}_)pt(Eo&$6|Z27Is$wbJ?ME>NcHB zK@V6pA$Wwj@N*LaA22yh2xgvUu{$s!PzuGPuWpUz3_TP7vltew~*p?5iK-Fw)HDI-noF4?YJM!|hgpU%+9Cr~WgRcOB z8LJQv0Kyyt#&Md+`QD%NQ#hTM=nRvE>L* z+GRyra@V>w>AUB#$M4ZL(U)v}w$fRM|B{``#{GzP@38-6D@nqQFWB4z*gDDhg6%n= zbaV-S8x5&Z`D*qwTUjXlc|xNeIiXU8Y1`Q0g9=UqKEdj~k2SnsPqv1`dWW!ze)u_? zbV&I~@cW#JA7Hsxa*Snssra+)A1HGLzhfMElp{Y>b_=tQu^t~`J8bh&mi!TVuIoqH zg^$p4jX26&bI{@Zj`Q?#FvSs$vWYoLMDzK%)#X7qY2n72GTC5bU*;%-x^)3hxlrB? z4o+Kk&(Xt1UcX|=4}BEhn-`Ihis(8BBg)H&ZPGo5*~a>OtTdi*1lVZH)8ss3`FD;d zpTW^T;{x{^@oVk)ks@&VH-O2@uIQ*Xs@tWdOWj`KPBAZE{YFS)%CE*(7kDj?!@7(-#ghhXX z2US)aVLLxj`uQw)aV0(`3+^-zdZJTji8x!f6)hJTvfOfvIkNSLVoq#~BV0z5|B#kI z+qRBi#6tgw?}=}ug!m>Gs&4q@6J%I(1H2`H!CXQ``lvILwBAitWZzhuP9kl>uHW4p&zX zyhu^I!NwkDw?4&6^!JacHc%T3=WVbQ7)LoSydo5T%$9$qG{z&aRkF$)pA$L1+`D!Y zy$60;+{bEsbC{G1q2EBiRpzh~`Cp9h zt?|M5H;4w`#Flye1Jq$`6UJq0@ut~YeAatfy#0GD{-m-}UjHtd zLZpmLSRxKN*1XHsUBPC}pm&+?DP^V*(ubv;Qoa<@-(^Gdm2HjASe_q`_v~ZO@|B*# z)P3xw0&IM_?_=u%DBrX{7mj54(68BTCX9*8Ggp zRJ-r~P~8ah-%FmN$r3!97U|=N+pFzY%{hbps-}C`(lgkM#T@1E8RZQjA&a#vRGPf} za+d1fvgS^MayZ8ha>91x*vC$*U{8+i3AP-`23s zrFfHLolEg7$2ynd;kUI?oF1>1qW&?fcUJLg*7(!vLUg6zfI$A>O2>vQ?5tvb;oEKh zSfqJCR~PAZM0k;&MbL418@v8r6zM@?LeX8z{^uf{vW*26p-5Y7W3Lt|p9*pFSffkW zzs32@LoWe8eH&YGNtrLC&t<<}!fxmrb6JzGal<@~vGHFktp%64Z28wpjPPz8EBzX~ zM7fN$y$rsdactmaQ zmS2Tse{5uXuPP%OeYW6v8JjjT*KZ+X*hV(&TgYgCVRF@*R0;BT78IH?UAloeRTudR;)~G`;2z1s<#8yZqVvWzEk^--@sbgl?>t6 z`E1ce#fKH!m0))6qN1>-*D&GeyPi$FhWh^`jlFjbz1U~??%m%VuqlnXUPt{OnZ^cR zNBu`x?6{5%`XlSu{p-ro+D{Mtv5ss&FSZYLfO@gMX3W&|C(EKI(o^sb$Cchv-BUEA06 zag1L}%a|sO7h~6j-^Vd%s>)^n#iAE0O>Oujl?k6T+snQvMPIsowPs*Mwv#C-Kv$cx z8xc|hQ`RBqV5?c<+t|l%upb>Kw%UgjSVy15RH@AV2y2)In1;1+j8z3tlAc;>>yQkJ z4ZyM=X&jx1Znr#g)Y;3{+{WFEjD76bZKZjOl2ss}HM=sv!m^L(UIrS`qDaAVif-*Z zxvlsH&IJ>sly!&m(Tlq|*6>uhm~lGo?Yy{(4fswOC;XVjvc6Ncm>0}OZF9cGF*216 zzJvDOFqK*F;7x zeUi*}-&H=5IwvDfr!nb|kcq@j7U?~NCo9>)@0D&s2Ygrdd#GJCY3mP)PxF+O;KdUQ z@qms*isU2|(4)*wWX3W)YA`N|wFRu#=w&##FfHPq zYU>O25@CP=GibXt`mz3Zi-)h{U7+wLA4&toiMO_>kY@vjHVFi#Y-x$??J~u?!I|Z> zk^!Q_26nPc@korrJIi-~#AbbxIWE@G4!KJ%@S;a9lOK2;^&xt`Mo(`FdR=FF4AJ*$ z^wJ2>@2zl3pG5RzP9Fh!p))-(g&3x43^9|y@SZcn8lv~(0^&hm?Mz=s^wt_ZdI9J& zo$2XBH)!;n#h?#xq9-2BAci~3R1fHufuWrf!_l2Y&)4WND?qPXLmzo`AE?4!G@I6prdy(V{N}KRUGr`|p4 zM^g|P2nDZJ^AwSS3aW6=W#G7z{B)#KGtgQ~n+R{jkb)yrsmVpTnu4<(prCSTRW5^w zC;cB46gVl6J8}j2)oJr6?WijGd76BmAjsdnRH{;tPE0|Gtk;i9qr|h|apW&i>qG?+ z>Z+QnTg649@73sG`$4})Wm(f|iin=9(RKd;z0jF{h3Hc?diq_^-*ciT9xWz@ei}ox z0}QL37>wxMXL>2o4H~`ZH_!(-)9(`f&Rc2*OO10;>1)&HcpY^RL%zn4 z=?;dvH4GzodD?my^M~)<+=86%?^rqz}N3a?TBf%Ue|Q$i$PgO!N3-($`PZ z7e|EER6lAws#^$pYmF`s0(~avcGKCOc+K17SioNwHa(~g74wkY{1l-=)7ffDc6Df5 z6_0n~GX(9Xix6iwSE%74HT;;u<`TM|vztpfyvyN54vRRv!eK6l$2rX7uz;ZHiYoaK z$K?=AG#^n(#VTnBg)i96nFP)I)ax3KTgPEKV4-OduC&clXj-a{Ojj`|eS*l$A`X`l{0~Z;!xVy$ zJPR<90&@rf6>%KK6NIX91d(te$H#E|G=fkN$?;JH&Cwh?ir@vicQ*~1dk}>5FoKX8 zP7ta62qu~X379I>JO@$O970zxdJ;rJFAjY;-j~N+Ig|m>7sQhvf=~lW)R2tOIyDpn z-*^zO>9MNiDX=8}8DJtQ&=JvW8Q2Pf@Xlk-Ud-dA1ikOl6{Hp`FNiH#L-mkSE8XUJw^rjSHuthQw3-hxnx`2~~t3)TR)Gi)L~B9FC9U_;?;4 z$LSL}K8B!q8jnSB7_DMQ@mLQI!w4pt!wJAGegt7kAdd$TG>7n*7l%G7)|bazIh0kb z2VEiI>3qQd6q zj}S!j*}>zPJid>|*Aaw4={%mn<4Xx5U6L9vG|d9k=@Kd5P zaDosN$m2l-p)iCX_&o`lo~g~ti}Uz!nlHis+Rd&!F7vntLCAPI4-m4S5&Ul=N|*?6 zu#q5g>;M5L;vvVyYKS~kqJ}W2ct4RWh6`i8SP(=W7N+7Hsr-pJ7ny7}NsTOdqNEJi1CrS;` zq@)|ip^gw28lzt|muGe8>0IO=M-D<*JZE#v)trw?L>Til6_3G%!%-@>2aknu7)}s5 z59IM69uMKri$fm{eF>hkd%LLlH@i|4Zj?FnAZUIDQ-Jl>Q4AHv$l(JGZ>|6=H05%G zG4W7CG4R779zqEtM73o{HEoR=A|I`zP$DXt-CT?yW^yWtD&8C(!UPl{QeH|BCf+58 zvOUZ37kRvhp!te=%~P)h8pS)CASC2)c!VH|dYcMS|1&9I-ltyE)oX@&U8G)<)N2Y| zA!HVZaVj>Rt}tet3Q;!zwU!fA9qOirXgU@{T;^$XMMfeum}rhx(WB@Jt9o!4reed@ zs~^V&au}pyL+FZWh9^N7;l-&wD%DrRdb@C{D~Ga5^`Pr{yZ6($h6s2+!x{wr03p4N zbFPpLkM%n0OAMcA3`Nty@FdpB1Aau`tkJ_>2mRaH^uVJ*M4zwGE8YOz2KwesV>}bD zYsi>4=i-HC;##Yw4CFf?wEq-B=++uv&LSk9SzEZ*(QZUHa5+msAK*;yLG(MXtGUli z0li%fJ&_cI5ktPl5WOA@b!!-i9!~VV8a*f-^m}hQEnoQ;0pc=`T%G8M55oBt$IN2 zFb^K6qT?lT%3%yK_4)7s7_c|o;^-PsuyYaLA3GfMeCSYU(RMiE=78_k z9JZlSF*nR00rR`TUm-g1^yfx zg`%cRG`hDIF8s>67~P|rzWwXZl`|~iZ~j$&GxX2-&8^|SArZ6%Jo#6))9Bt_*b>Ul z8QrbInkHbQpq8J=tqVCxZ{k1CUw`o_~|97jp1 zs|#N(=}RjZQ5`RH>x(SCvc>6OU2JxPU6WczO`AJhOqGZ?n#2D z2Rl>WJzp5vkXakJw-Tgs_E7^kZS#-pdIR^-!n&VWXIJ-q!m40a?CL&1IH9nXZtm?3 zdi^8YP&fCF^g`YuRw=qi`DVX~&mo56!=O>VK92C1$CXwD$^!@${I!adCb3w_y^-)_ zB3muFFBRT?)n=02hv96Jcs53H?=0-Q$JQ$Dxjt{*{kf7pJ^2ycdFi)EwL685($ZN_ zuCDjk6nE5z4i2`_-94z~A9sk`awef$+UIoMvo%9yJqmW6<*6eo!S3-fX>Hwh~qunw)?L1NC@|jF{YPQOE*9CweTpFLVu#we&k)YzFoL$IwQQ$tKwJ0B28mmG{Y3W_ zWV)}gxcgs~=c=~0akHjUUcVwX2)}6j;_yqyF9*LO{3`I1H-L^`G=4ew$?4$3uL!@O zjlkmzj1C@9Uco{ELE>m3nUpu#xGs zUf0@Gsn-RW{?O}QGF{c{F6m9SiZ1RW++4?lW2OG8>jO94GbTs5w_#Ug9g*tvAPdTreDYE zuA2NWjMF`=WBPNf&Q{m7Wt?tjeZt>%^?ZMn?mf3%`op7icSO^dqjU!((~VKO3$h7> z$8yURqjeuEruCzB2i#4EM(g%8{L2K^)Mk`AyIJd_qjlL{rf)~aNzHRo6(*4=Sv~`5;bX(J@ z5xT7Qr1R_c-rGj%tO2Ig!*zcKm_8h?`>+$?R|J{%&DQM+GQBff_aMkr^qTI^Ad~Gi z-G^O?n)D*c{P{)Gwb}n4b8iA3MX|OG_hix#FvvIzNhXtII$>1;L?G--BY0Ruje_jx zup}%Z6-mM#rV}Crj1bmBP#h2y7cjUms4R^}4=QR@R8W>dK|vOyh@v9=clDB9PCTFI zy}tK5|8U(@J$F6z)Y4BaUEMR`c6R1J3r^td1&R@F|Kk&+bl*N73gYgG? z@|Ux?6}|X3?&2=?;(s5?J=FV{0|`}q_!E=3&-$`@J$oz5{NAnn#gW{OfAAMZa@YRB zx-itAnOHi2|6n9{Z~(u5B=_7v#$33K=~oWow+2UYrv~wlkK|sso&S6UxBGT}_XzIs zJ6Uq%PFBhHvT%*hX0b}F9DKn6{)M5e2QLjRVzu~oI8!|_l9f<4p06CwZJWSv&*pYb z;LEeQcd}Xh@fKGRjCOYja@V-tj!w;x(4POkf#WXzvj)x*o;$k&ux$A55zl9#>4Ex3H;_nZi$zF%0>+%0fmu+&;G3E}G zu*AWiPjtK&!>>wmEX24^cCK^ro0FZtH|8HpajuEue@u2Bj^i&SJ1-~juX&wMdimX6 z=M#zi_C)SCSxsCg95HAnoR^QHF9m*ZPov#|nJ$eVfd?fevU5tP9 zZvKZ6+*^0^r$=z#-OV2x!96#aUp<0*doW))jQeUR(|@Diu*LW($pfSK?e}rNjN(7P zms>ZQe+<1pnm>3ScX>4b?tN@BNgKeI*5w{?@Nyk)uY)hC%T}sK>u`@mH9`E1Is>RM z(Jj!P6W!Z(Oma(g?mSYt0Doq50r?GZzrBE@19t+Gff8T?@H%i1I14!6SwND3Zooaj zG~i+2abU+g3uvn75oqqQxW^+@9M=e_%Oc%Mh^%ob5E}v#II(vL!QWfpQUJ!e za_5m;GLI~Zd$i}KTjP3tHtS3eI`&vxMldr7O=x5dDkNPIhdXf9;|obo7MH-^Ur72P zek0-nDI|jtzlwod{$L>)jyS7@t9cz)LeTkxI2V*QK*(OC8hQrtmykhzyO6wyIQtQL7T<;VBj82HeDXHp`Ouf~N8}G6 zUZBQ}j%qQe>p~&|Tkc*+)(&4tLfI=w01yDSPh+1WpBK?*a@}*-?>>j{+h|z~XQgo4 z2i@Hr^oi+iKV352-8lTu>Fz>@D=&ZS^n5OR8oqzdW&XajJH?l&M}1cay2Fmf?F2=$n@})qat@jPfG{I_d^(8=GVL%T4R?LSonO~8ZfxG<@%QEBPoF$(V%M%?^YSK7oJN0N>F!cDjcdz| z<^;NRmHWn+(P`Z1(cI{^w8LsQ{?x~M_dvR7wL3BR`m`c4czO}(1_;2b4;GOh!TEve zRz+k`-y*UOXajWVQ$(irEFx17pW3d79Bp4j_5vG$9>C*mu{8z%T4oVB)4hni4{Qax zbt@t}Iu((+J&MTvt&7O#{fkJ`0m$38C`j&ya1;iAM%ZT_6c-keR={##_JSfZdN$G# zrb|WSqoN|>27X4o9GF^&?J49=KTG5y@F`FM^aXZblE~v1B{F4V5pmvMM9xmZz81&^ zHiG*or-k^uii< zTw%Cc~H$G84QpEP4bW}kBF`Fh|ngF za6km{uhWqZh>&~q%_DRAhQQ$?yzeM^6$a@iB_oTgn7l9t&|6K!vr!nz8ivm{x^ME20 zJP1biAl?Zr?N*QDyuf|HH>ijUg)Kn5U3ngPeR&>v0w@A*0Cp_PBL@+_^E9s5vw7sM zEqUbQXY$BI;PU1?k^_13^Lb?0OSt0O^T?YOc@0P(5Z}F+M<##>S3mB4A-c(>pYzBS zzF1)Kve0yOtIw=dl2S==|%>J#|e zMbg+}B26eJ@_d9+F%cg|{E=e9t%SS=SXWF!&leM+0@zzjB7nFLp^P+!50L(0am`Hb zrP2%TShqYkpNRAF$z4F!oO}|ROE+=kcM1@481i72pMG9 zU%^lM|<01+GB0uF53CIPie#|EwpqiIId7SHKR2-ZOVA;!m%%^{BbKe?_ zy}N+;fQEM#ked*80r~?M?kXUE0_P{!j(y-6R%DIk}J6p$mpKH$z_1*8Ws z4%jrbfV4#T3c?$KF3_Ddt{_P68&^P<19t$8fhgd|u>~Xoe1Z78Kq#w#>_+$;@a%{J z5(LtL1*qUKRQBW11>|5(0cix3Pb?s*6AH*@<5k2UWcX@|igyq)bU=kW3=S3$*C;67 zTR^-T>LdJdOab|4wu;dR2LOKHcNpFetOFKlh(QlzPExT4Mj7g(a#x3==YTJOeZcd; zY9I(q208=RF<}?Fg|%=k!hGN(6#f~EY(}^k_y(8@+zWI9ngNNx^*}KS--(_wV-;jG zI-#}99>XgE15cvY#uebdvB1`m$ON$N45Rk_!Ldp)Y()>vhmpzmtLaSV1pLK|Ys}yQ zUxhq7P@$*MfH=gbp?5s6G3@T}hVAa>_@R99F4pSOFfei&jb`@aCFCYn+z&cq(weL z9C85Z;u{1;2Um7RFrc(z5XkA3JvXcN7t)BG3?%fr~8h&;}tF{oK4@`jk9ezn_ggH z(geCe&k*%~Hto0vKcQjuw2Y;@Le!L^T5k0cU9UJA! zq2DtlZw|eFFG7C~J$4kzS$o~lQLS_6P)2slp>tR$=Fkl+9GF9=9n+Nt=g@B%d2bG_ zFC(0sL));hActOMO1UyD;kUPDFtRL%u4Cbf9J+^vkLS>($K5Gw_Ce>F99m}|xa~Rg zCKm3?p?4s3zMoS}&wlGpR=U7>5K_bM+{ty=0(C5hzRpy?L#+|kyNK4Yg{dz6agW|- zS8{0cH=)B#qyt$PJ&{gB*zksl7?RM%c7)6LqJZvw%iXwPl8MaPrjo8^CbHt6aL9_% zIFIYb@kV|@*4T!vOuDX+Vj%8%6IcG6R#JZxne~E34l|LFJsO!~A~T*}G4nK$1%n}=eDn-uR%gH5{{oQP8kHXdw3vaL3RI9s z2)*3pGl=j9Mm9cGh?gTxXhx46bdL)P!D4bVkO>?CZUqhjBY|pQDIOjjM3@ci2POkD zz-+Nk^+=Qf$R3o3GO7USdUPHO8>3I~$<@kc@lYJNhAIz{?)}WY`0iQQXqp>Xy#fj= z(d!#$5VptG8z^l8_c#hf$N|Et8F-F@tGc$vAS;6lZKUfycjpX@!0wO06`-^TQy`EJ zqym!wMH&B9psZ?ugS`xdi#dHSvsQZiQIu_;^(>(}pD25)Jru52Q zRx7)tnASN8wN=IRCKf(jOz%K=!)uy4t1$ZJ(b_A?D13 z#caAxi~72l&D0uswwO)Vns5mXs}Zghvxz$`DrNzjvr}lrG1zFdfKA*f>@15*S->W5 zHGb0qHi4%_bz8vZ?iBoaS}6Bkz$S1_=(m8);wjYia(r~u-3!=Mo)$HF0h`8AI#bVC zz~=B2Dr|$0yMX=;RaL{izIG3A3@c?f zo9qFH=4MqFFebl8S(gyP^VnRl8E`pflE^tsIzRy6fXLYy#C5HvRZ3-Z!EtA2(Bt2@ z$MzGJV)g=B0lNSZ*aBn$V*w=x{>wsv6+jgb0T?eL6n??vaA^izbkhA>)c&RFpsYeh z)1dsKMpOo6go)~)JiAmKls_qyGAOSu)duBt%d|n6yi6UG8Otn#(%A_L*fyj(MMSV; zNR1k}jO`XuXvU{-#=*Its^U}SBiBK`&7GTlC=n#uZ4he_e@ejKR)4Z6j$9~W=$U> zh2w6RNkR>1${BZB`v6WD9t4=#wFs4T{3mI75)iMBRr3qr#0E2KP8nK<|8S#{vL;$1|t&VIMgoX3&md>GLaDq}>9i>}mk}b%ucbhb5Cdueytq&`) zx~G*ZN4`)`x}n;g*p1nCtw$ds{81@~vO`d3zr$c6W_59Op_QzP|AXjE3xP>+YtB}ryoNllj z>1O#coBmeF<_3gq>0hihR}g2N`)nBoqchDd_|RrxBPwJrV5?1z*z`}^{H?!r>Fv+ZZuA7Nq^6fTxyiJ~Klb6`!H8y#(O@7so zmGZf_41$%Hu(peeAOn$Zm{-XqD{WBMpnz`y4Mg&`P^MLd7@38W0RNIM?GO}QA5b)QIKkrZ?@_8flrwAvotxV53AoT!vg2pHo3da z;77Lf+idBhZSpjmT&T*ynsL3@rm)&3KW&p=vB_`RX!Cja10!&R+54*u(zWB^ccVkS8m!ITUz{yzoF z*Z0&+(iOOJQjIgYIpR9^-^sd zTl^iR)+c9%pS%_5gz(a`I7ap1nFMybJk)G=mjF$p5P0<2+gd<`W65!8UyUU&lWi^g)zvIJhl5i1bch6gM_01$u($_Q;OjwXSv z%2StQ_N*fM&MFF%XpFEH#Q+uSP}F*0A2=C1hFvoWp0{yTJJ2ae*J`B%YzLCHO5;jfqoQ2pq?NF~Qp+KCKo038TmapkmQC3y%~NxT%TT{%T8@g1 zanUWxx1*+-J-r-r4%Z{dxm3?luL<9L8wi-um04)c5P2BgP^MRE7b+!eMIV=0b96?o z>Y&CUhxo9TL&D8zi;OL(CfL6$qY%C`UoWE+%?Lb&YpdJSvYBgDjSg@XDmlD$bALvG zsvxjuaX1s_8%+xGc+jTKM^Ri~#Kl`?5e}#l5t=^blix|TWp|-uyl)n9bx<>w z`w-r*##@>y!=fQC^7Wa;_UMV>eRUcIok8)nVv-Id0MS78PTXGt`+--05Ksop0ww@i zKzE=e-~l+`To?ybKqXKCJO-2k`9J^|1atSo8_HQW4t~Rx+BiJ_=GNLcc1XR& z#<8cZJ1rc_XD2xOLFS+a6Im_rVRy5Kz$t?EpYjOzWwmGS1jkMYn3v1ojc}BtvR_~fcs`p}Oqo4o`iU2E2|2+8CEJA?IKsFEpMgc>CyMVk02+5z+nzo2* zl-fQZ8!cr9a^EzJvBI+6MN5yNKxC{y+Prdz*t0PgzPBnv2SIGaQOmuoi31wugJ286&qKyuF}nT=+X2|zCLNeC-| z2;lG_4PZ8_>LDNE5)f&i>i*Oi<;2e>!ESN0$!kEp#D9-N$jop*GTD)rFq@nQZU>(6 z&L)k3p+GhYVRrW*bOOgwnQP!jfKw_~e|Aubj0f?6LF$QQrj5H3@<_*gL>q@6vo!H zVyZB^<_x!SSWIptV&k*FnA8Ir0ZBj#&>H9t+yx8zz0A@C3dw4zXyH;>b+M?yg*B!4bTDT31k7|fgE5OFawzN-i;^(ksz=DSO_cu zmIBLxRls^+%X`Y*p7!Xa{0!#l3t09LR-aLpI_(}pn1m-Y<)@JhL;#m1S&nHG-7o-} zV=76QFq5<^qTGQf$r-gJv$xr)^iXOnJ($)g|Gudouyvd5u%fulN}TN%)X$I)vA+U1 z;0V&H&Z8(60`dXup8yfySo{a9*=UG_8AawW^L>YXQH)Y0@A#~1Xu`L%aC1JJR99og zK`38E-D9w5KrstxRe<~rZ~__6!5Gp-gjs)LbMOn&k(PT3s~C&_f!;!a)hIB)&nChJ zb(28Frj1&5l310sZ65a#i))`|SW@h39g-@U{zvMYj{+BHjs>SB7NRDoD^SrCjRCl= z3kdi8g7ERCjZR%0xiMolDQh>IbOol~Je$;k%;F4N5wF*2Ht7biaCJu&4m0E<@xk-q_^`*%@Q zyKeAr8~D2g{goc8fTT4rR|~T~$wnVR$j}ITX5lY*yWU^Pt5GNr9@Wfqs$n8uZw`q7 zKi8c@ggSFbRg5mvk(r*WaR-y;U?|Ta0U!iq0$gHvT1U@NhkHibIi$CSb#3O5^0wj6 zJ9&PJiaR@C4tWX)0ogzRs2&hL*42|1-RHL_=a36q=aAQ4oI_aLr-dxe@bNY^)Mb14 zk8N|peR_Fj^S;Q}s2mUiBp?7(AD=^h`g#uiqOa$$zv%lp#C3KKxf=)oxxj zT=Mq&b4i6fmz3?BOFje+0~Mdb0MaC2#Ro9@;au`D^rW}uk|jWHz*b2N3ybT-%z6Fq`Jk#Itu%~~6j-Sn&N3sAGE&v_{_5qAzFmsIG z3OqV5Jb$g{X-CHikIW-ql+7b7j0ai)JvEN;W{&Z9DLUbi>pU0g2PeFT5`cT(wc_qt zrjZlbE+LN>vvtFv99Hy3cn7$&Qk7#9 zFyn@TYYL8ikBLpbjLQMnLgO?&#~g4iHIAKZ{hHigz_G5@TlXutD2(X%6dT(zZv_``%lrm7?n$+Rx>vso&SB#Yf{OxYuJkdcw;6fa zqeH#mGfa_@Chjsgr;X!>qPH|o_i8sd-lo?CTpb(N1zcTI-XQb-fgodSibKKGvvK!< zt8e3`fV&Qyxpn#AVr_blfD^!_;$M{JGnu4>H8yeVj6WG%oW?PG>{&o-jl%!puxdiCpue2za&J7y_ zq31Pmjlhinm#7uYhKm#2Byc{13o2wj$Rv$qh2WceoCGczoVi<9fn(ouW|d|JqY*w1 z&Tr$M0VimAS&P|cJGfLEw|g{Zp)@TsE0~!+0L3PnqRt%!*V3dH2fZJ_-3U&f!Q#PP z1($BpWA^HeDIu**_R_)mz}?Kapvh^{LAC>FUV}5ib<;BI&eaoKcN=#rxSrrxW%QZp zZg9Q8)xkfV8v{<%^0LMS6H%KykiE6cjBEyO9=Kb;v94xa$O>O#;+US!^|9%#f}YOl zdW`F<6s+1~4*bNJk|4R&L^9`k9*R20GV9zxMeo`*_T=eiY;9hLp3bof>KuCuTzBf; z;P*lAb}cWfa2w>^5AF^{5B|>z>5VKOgUkY{Ur(0#n2AFY^i=LH2xiCp4tjTM9JA*~ z9WI$TmRIKnoAdZaBFP_2ZDQJf!bTF#kj_fA>alqwD#aw zaFZ9Q4GC(B-2EW)K(el`t7V=Iu34$M#T@q#xL(-4vTkKhTA3rQ0LO={dh5Ws-%)q8 za4|*i6>yELT#(xf(gV`mU7vt!Y~xOvIMtxT!MHe0PbxMXlwuU)+s zjq;TmW}8U8){IMmqA{oAxhlBP;8^SQVOI#wuj%QRbE%2b?U^|}Z;+9KEpy1k>6y)( zo|$o}$jdIB-r@>yX*O;*xF$C4J#bCm(e{6O=A$5+*%Yh6HMen&vAF%!IKBJb;96>& zR%wS|;&eU6-3UFkSJD6My0-y&6G+yrT4lKI;96;%-s0QArQ5hs;96^(-u(}NYh%+Z z1lJav+0mB=L1uttH|TmHo59^|%e)m_I~%tLTzeZ=1+D`)R){_#j)2S5^z_Sl7Tid1 zSpSS+@(0LKHbwWi5;6uHn~*ev+>PKK24`+b7Za!IIUI~D)AaN?<#uq7fMX3tZ?U^j zjvH$t^%^p6sg_wEic`TYvl*NPu3Y1EKUxfKxlJzwZiP+nrE%dMZ+q_LTWqMFM~Z<7 zz?mOZm^qeqTlKtfxA#4_I)YWd%_D(7=HXjxIC%Pf9$5~22Gqtif=&ebEKkLid1Td} zY8XH~pvCPR<6S^Le(fa>zZZZf<$2r?2zTb>T#pFmo*JVvF$CpLyCu1zTnok~`XiXjh0| zcYz|nIDdyias%)g(wO}3@HBLoK7(2B^PBPGKtOxo%?xXrrWaH)U20oM81&36Ba7>y z&a=?3>Alk0&>xKWt-TA$`tE9Y8^TUnyonYzM7T(c=OMfgIMq4`#U6!ZCcps?_ben0 z0LBeLXy#r-hIcfbcaV0xD`bSp2wQej6Uzb9X1F-a)5uWa>R_p{S$tqe?!DI-3 zUBmEmhdtByp4Br7$^QI8QVGbwE?_6{3Qz$=Sb71{0U6i@>;zr`Dhk3wk9*oVaw^sq zl6`9m$rV~il7M!IKeM`!TwVnwq}f5iH+rg&RBc89Pr?lm<|C9h;i7I-(^$G0y*Gw0 zL_Fi1&Igy4(7`7?sm@i1%slCNG`Mpqj#iWz2J?aTk-_}~lL?!n*c5OJ!UHCbO%*!F z{vw<{m-RvH5cJshc=Wt|5j$n#SYDkQU5X#4(iLw->Ng34e9UH2EV(cr-E3JC*u+Q2Q4Up|iua;}hka<&k^WmQNVXCPlFBoF>sNZ!&= zjxc1B8UM0P)^-0P^wb#B6#{=2hOhg{^OPh0lgF|5LVL|0aN&Pm^gJ6KfBz=zuC!Fv z)$Bsd-lRM)P`=0L4a=83Qk^>{_nc4e1MUHC19}6^dd(-vKx3c*zymM#o==_v)&t9d zMZiP1%qN9F0q_8jeG6TB>OK5&h?razFxdfec{r0E7eQ69I4ofdQ}1Z(PUGJUeSX z83Wt{+z#9d+;kW8fH)u)h`NhD=V<(;^SKvH^V7A(1*F-Uac_NV^MhKpKYlP;i-ucR;*7`1T8HtR+)Pj^l&KnT20xV?&C$gXrhD z5c{<@h6xLaFbBWd2FT-)8y_XS7goorU>y5IWVXcP9q!pwLaS7N0 zh%exm;Gi2X!dPhKV?-MNnDT;hSpr^-7_L@xc?NH(WRzOg|34}zjWjALE{Ve}1G@-l zChN+qx6qXhoLp!kx)0#)M{~8V1m*hZUCFwt99_x%v{bp`tSc?GWD5VCT24h%v^u-n$>z8T_%XrZfIP@>OX)Val!icW^#84Jt^O)x z$hupGV2dFG9D|@iMCbxIAcBF!`ZCgGVFD3vH{6odQbJ$mb~Rn`GF)*%t0hzTFVr%= z)>_Iv=(^xA*eg<36H7ycJ&`IlWda#`Hx@zS45iP#Mu zhLF=jS#tvRYJgXHseDbtMsNkxUyfTwLi?60keE84FT{GfaCJF;VPv_IW>4qRmf>vx z40u57j-CX}`B{1(6O#a7OHbff0b*+?bb>#U|^CEEs80GUOza>|IYHafTdnL#}Vg0Uq+zbx|Zldmp{2moqeRJ@bs@ z@qsah66>SDD4RUODn|yZvUoQtaHo+b4T5}|AqV=eCqiHJV|S$>Wv`p;3|!HQ6@D^Q zS+PR3O}=22L#H+QJ6x8NMw)m6MMMlabPV#B=>1RBVzCN`EDsBK^3tY=4 z>fBcWnIV8(o4#3)?!hp|M+xsme6R*a;-fEcf3@%0348j!E5vq#Sm3bl!U6|l`ZCNW znNr)nOUBpVg5|6>0CTzjLM<^n*HTMqWH@n4+?43xB zN*_vEEmovk{d zRhaVu8EF6;%wF30F25@%EH_pHW;(LaDu?FTtTH*^ z#u^-oqpd}cM_C=lNA_A$IAM=fj=XG@L(f`e`59G~HlwXi8flS@kROM<##r8(x!BSm zk5re45JLRyTwFpYfpr0!vp;=Q0XGW$3E1cK$i#JoK}H%-vut{u5pJD2Rfc|fwB~X# ztD&E5@=sP-IIqgmS%hbdH2D^IqFpTx9+Rt-MEj|D#_|rfRYSD0Yu3PS$r$_31-<;^q#MO5yd>Fm@1%S|~rD$yz8+)ntSsf&Tq!C~BcR zMU%Bqt|>&>p2ZFp-+;ozMGE9+U;xmEVG8RWqziLO73k?~+G9Gb`?v~J;Ls8U@Tm5CPEB|3mA^Xc@pg z2ZsUv#rhYp4(RFsnRW3f>IKNK%wV@3S@SSq(9`YK#kCLPxa46XZ9>m&GWWam4(j{( z!wRJ5kqVftv-Hr=G6j0N-8whCj4+5#qGzA9TbD(QWuvCGbZnqv9yOc0UPoZa@GFiWi<(6`g!mtf-z-~SCJ#qtjy4`x@EH)#6_%v!t zi%R2~P^mI5b}+LYIif;*5ov(kwlH=P#u7%emTtEl7`KQpa2Vx))-T7?>$9IIZK!@Z z+%>^}&Pk=?>Gc@dsV3GZoI9$?&vk2LL;bXK&CBsI^zvKo&U&PO*!5d;7AXJbd7sp(U{RWN;JJb4s>VTbMWj7jrx=mi7tZ!b!(5 z9Us#!9F#WX%qTS!`Wo?oh&j8vA#c+8NwFHztYLzZSD9jGaA`eG8AW^jQiST%Bxo{fCt7n=(F)KuU` zBOZ7hll`NH%&mq`jyGhf9nyiC0$VR3p&Jb@&rEOevnJC?Dj0qvth)0sJC?puNvtU?lw! z5^8C1fu@)T1f<)>e&#S?v@xP1!!Vi$8?tmKX7m(8=FY?FIir-oX_Ro%#6?gm+q^Mg zSm0a3g77ugiKB)rAI6%p#gN6BNS|TIq3M{JrWu?#1!=Z9XTY$?Xk$(Y4aaIR#E``- z#QlaWpT}AW=shKz!RmC<;N%EaK-+}Xh2q=MnCbcWBk4}@s4sVU7Ey+bgZGF)+(!I4nr4I24G?#i@%QIrv zWcn5-q7G~&*)jg4 z(aiaseZtNLz4W(Ybm~c$D_=wXdp>LEraI2AFbxnpr=PenWNwMKUz2c8J zlS|RDYKG3p;0mzY1oqj?I-K5hXKVcSd}W%8zEC$QIq`z7J1@d&qw5|_Q6_FY<79RZ z^ohTc;S+HIRl6C`HSBt_G+i;XdCW601@Sj~C0uV+bqG|47Z9_vFievP2#Q^=aXZ-C{ z=+Y#HmcO5#5ym1{?o_&-x^|>^siRkXFSa};P*zb|^;KFl9TbM4(s6oYGvj0Fs&Go< zL@by})od3udc5A$Cj=LDO-t^Qh-J36PC2P+st2W1I_US%)4!#9=%m-;13nS!vzlpx z(mm`Y5apvTmvGRNLTotp_4q4MPRX={Hm2h4_?A9ylAhh_;*WV0kH?Ts(>Nm$6$ju@!M}N6Ow%5$9k=-13LCI)8(U5p?4O|IDqoAiV~w~U}WnUHI}>YrdTRU zuvWDpAvP?xNNDLy?vD+b>QUA)KY?x3Ok;j(pZMg&8eQv{UsXrhJ7LU+eNCw}v1v4Y zVK>}ddd83PC!w3F)}uOsByUVO(kfv~hL5XTs+DS;5e{}zErsRL2{AYOMC>5d%2{WF z*zQ_^a(ru7*tIm_kkcz+=c($dlY%}y4x(d!#3c)y7n)M}v$Pmm5!*U;O&>o4x@fjH zHY}}3*y3y`WBY3KL}yCV5^kiCIW1e#sIUQFT;ftE-f2AJv6gk78YlrLmtTT=7cS87r!#wh>HE zJc24J^ZTHK37Fqy?4uRUDYKM8tap9c%bHQ;7r;v9CSn!iUeW7m8(Cp<2g;S+SUPC7 zxBnUe^O;cJTZ;8>r=HX1{kzOLvHp3e>s)GdIN^G475p3ff3>4+{>|*dxsLVMUqT6fSV^CJe$c82ZBUv{;(a zH7z#m>g(MJN5DOcT0fhM)6|2^5fle6@E&ryB;4t!nyQ1-;k>scTb3|IbV$Te`#1jB zaA3A~KU^6%MnR5sclH4DM1Zv;fyo?A3T*r96aPB%BPKb2Gz$mX<|aK`1tweU+E&Y0_D^2^dh_~ng0)h|oYVAJf^+6TYfa!Bhl<<23ujW5B+ z;ha%1XLF*ahgH=zdsDEH{4+g<%0H(^)AH#_t~HhJ7%F}z)JxR!a@Gc%)bdiXt*_K4 z;xtpOuqTBg zH)i_$VGIV$+}u67k~W+Ub@Zi&E1!cey2KHBnl^|oclGt(@gf@8P{Q$^QRmLIQ!n3u zu=p}-zl>ABT8mv_oLl4EB8<{~+)7dSC!*&Qt*QhH__tb3a5{PX-PPzxQJ08ZP0C4M*;VQYC~IFWSmJV zwXzKZF{G(!$3Po3HCr`oa^UHPQ*d3s%sqs zo^P~P!X&ykCJDztIR8~utwUf49ec^=UvodsLz1G{t<~*G(Nt*KJeuY@6S2dd6OVmx zd<(e5Vcdl0&Ww)@jn&w^BsL-8AiLIz-fcX_!A6JOruCnRhRHYtSM{v}r;MJco7ANC zGpMW=lJEyehZzPl!}1GB7%we{D;(^z@zUV}#tWV`DdugCv{~0w#!LAHUzbEYrK+vs z6Gmt@X2Mwl?q8zSH z*LAHU<=`f@rD6G>N$hw8&n#4v)=}a)sH$PhkP1irE$K>zOTse_RaYAT!*?X5)9AfP z>EYa)l6T@z6%RyI1#J|}dOfLsSZb4uQNSJ5)6`KA-jk&5tE$>1W4kY&(9>-daT`w{ zlXrAV#(|%NXENqu^p#@}-T2QWe^{J^@^Hk#1Z^|54U?hU<+(td!R)ddCov>zG3T}peNxqC*3Opz@7pRW{9H=ZpjWG(& zIF+a}2Et-`3a0xlUuaENru$bONN${ncLNkNHveyWNYhmOUwPV~Yufz(q^7y%zLp+( zq&psL1=^%c@`-ryK&_v3BydOcDjf9VCS{E$fu{EpV#3|qrX0X6gPN+339JD0+|78v zmenrhkWW6Uo3suL@tCQL@CR71s#on!`7QCdrfwSx(s8{8nel^YZXC=9(s0Zsufppf zRm0NQ12`(Z;a!}Fr#Z6!& zoOH-8#;(C`9#2ix?y(M%L8mQk!66u}OiO4=15L3@&AJX}K2q0|Ef|$}@~Y;yj+wG^ zmi$Z8JT#*ShsQz__B7$1R@lw-OxcND7oOs(`K<%#BzwAR8c52n3r~GjUF$&dq$uxw z;LhWpNge2sewf+BJ}DSY5*`_=8LYlMgq~=Vf@dG>SzZ796zpa2d|6dgN0jcwWprbQ zlqTVlr&8GFYKX3+4Xs0&DcZBT%4ea_P#mr+u(n`_)g@)Xj%_LIUIov#Rr^X-+No#C zUp%GD68&D@v;*1qkJZ|zjFyAF{dhK~*|Uw7zT-4aW3|vVZKI{>c;j7} z`WQ%lkh%{T5s#%5p(n!SqIO&*Vy2Z)l_ZpNV`%_ z<8hsC$U0=CNv3M3L#Dia%4IyV)AVg)MxLw|ykjq>5j>fzy&DEi)2gT3KKGSzaeOi! z-m6WqI(pf3OAXj*Z%HM~z~$)~te$;fh9v{rKp)tjf@jV#cx6B>!RpFQ!JA!KxrwgP zwYu_}P8TUZhadRd=4b|F7J+YDYXa z{P>fnc$r`EhfLFh=K~^)#hlYxtT^|Oo$(%D0Pk}t=55|xqN^HPf4m7+TdzzwZnV>9l704{tVj%PtS_rT-bGlvu z?kEZeq~Fm4fV~2tXnY?9FsHNfYbwCi!IxD4@elL>U^fs+$5&|pd%9i$u?^Ay{2qq_ zdw$_we8&|qrL$kASDj4hqR!|hK!yPZyMe$Yqyt(yn}G23)lKj!(??iqlh;_~P`M^A zTSlZZBTZZkxzr>-1g9%D^u3>8+Zz(Je*iB*mq zP-XFbv}?bSCcO>0(j@OgaeE9o^g8613^`JPzIhJS5w@7C6S)CSVfqLzs~q92a_A4k zB+C~1%_je<$-khtE}GJQgnYpypF?k*Ms)(;nyVuWuof5SXO)E>RyoqnCTG~>HmWSP z#<;l2NE2IPtTZ#^Kq>}ja=G%w(#!r{!9dVRQ<_<5l>>QJS(t2-C)(s}RhGsh9BZUW z_aZ*pkVC^U2@ENZCXqYJ4W}6&+G?n<>O`Km%At)`IY6zlv|5$L6&U`@jWl5?=CsA& zN{rHajE@{PG+1fEA*&quz$%B{vdZ#4RhIUklXn|wp*PUDFyxmkrA1-Hn?8cWDo1`X zEHJguIjbyxugcPQDD7J#O+JZsoxps2%v_q#%4$7uqg59CRypFa%3LE=mK$QOyWU8X z8(`wBkNK^x@nyaqV*{fN4W;fQt#TmCDhvOx%3@zl?v0sIG^KUKS0>w`MB2MSs&d;P zj1T3QMi&Z;Ot8wKkybg7WtF8nR9U3TjH4OCYT>(;vIZAN{Q^-~2;&6~2gTW^zs?`W|9B3`@-gWIOgJ_$Xh8nf;$#XHn8 zajTV1GuC5YpTE(6Fv=&Rr&awdO9u#9TFda-q;fnF6W+YpU*hn}xKy>(wuCx5`+HHG z^<($?wEsvwFE^@o%}P49kKfN;GEJuyd;HPistW(1dOk5*S6A-}^n2iv98)*!c*8#l zS8ZbLs(mM!RM&iviZh~J{^59~!NlXr?@_M&6y3&JrQyog;W&Km*E;GAKeF%jAHvc5 zR86(qcY>34*1UntO*a+C-Y-p{GTw-zxsBL+f^BhKyyEI*_>$deJu~)!jY5L*jo!Q&p(X^>gXqJdA zj+)Wt$YpBAuy~w}Oc}c(Rn6KEd8uySfh*$r&hJ`t7%wSST*Uhk6<_-kqI@FuRBFx{ zN-Jvotz69s|7l4B;pWld^i%%saJuEXey-AG`lXOpEBfmBZ&?1;-;(!9*vF~1t)nVJ z&AQ?_{}vn~U=OJ3W+-;`_jdbkvglq=rV8vIRb7{&tJgVtvq@KbP2)22e94rM85Vy> zflldZLpf7QaLqdAi$bgH>UfhnIkr)%sx{A?pgZ!M9OCKHd(PjP? zG;sW;^jFJ-q&hxvyQYv(XmR?`OBS=r>(+IJ$1vVr*6U$)dhvBrh8^{UNp-#48=9)k z=kc2yA%Cy1>=NMY;$GdJ)knmACVN!sD@-uFJ@>G`DXmz6_xA8+%v!vwfi11l^ERKC zDis@=&zHpu@Oci~;o7d~gts+aIZ?m^!5S^AgG>8$i^u&_Y3_WScM9M6g>X`mFcr4n z)ookb!o9EMLNm0ApGy&vFj%oyS1V(6^U4Ei2E)y#rV4O#@dI7o>gL=hioWXRJDLda zav8S=s%fj23kOZAVX39C1^N--vIB z1X|!-8mW!Ycg=vbrnG7>dLvMciB`J78yzlhB^-+JamRJj)t9f~Md*%M7I!mh z?^?Y=u2yx!@QVI0i5nk9O?pF_^7JZ*7ge<#{g{@FTcui!UDAyWP>kWON>#HuR>`lb z8XPMJz#ab${S1>!eS0t%IFuB9nMSf0xqNy7baZC)we zr0C*mWi@F^BR(N1EX^0N|KM8vh4I#EhOjKbu2;f+c5Mz?N%zGFe#JefE>Nbtwz^`L z(uw-82xXWm%iJ>%4m>3EcX-8{{~`k|atXcI3hAK-)+OK}293j);u7F78H0uV%lxgqotPtju2ez?wS;XOn+G49yiuWpoB=2}hnf`DLqG;JBNjacuncPu+g}I|R z_Akti*UW|$vp8*03%5>}(b=jN{ILT~AByj2$gc@4ad~h`qv~f_oHCN5=xf_e@eMW$ zNE81;callBR=3RE&D>JLiIG~C&B5|a`NMEacDIVtrrLB%3Qf9H*okMz=?hY0!sYJ@ zxVhuz>$$9s**9p;1;^Y;LwDn4TIry$#V6z7OD)QpDZ0p%3H^o@_A@qRN+uqg-7nab zDbanS;04N*DIOMLux^*=R&y;*DJ@p4;++`gmStZuw-lFXx;D3rJYuQ=6~AthL^IA{ zTa24N965BuElfq*6nsPFriAEl_XxbyC*gEUX^YKE#pNbTVchw#dm5aI)z-yLSEx1A z&jRp3sZr|WM4Y3k+SYxfxLRwEGJOh_7&WeDxFGqb1$N($Q#dtOmc_jUs^`jVj_)6y z!;b90S?tKFMx!bF&k6x_AP)3ut6s@QQ&ZRyi)Xq~nEFEJh5hh=O||E$eBPW3PZ2%# zuX<&D)I4^_Wnl|e=ofSwR);9rYOzu1#q<=FIvmU6w%RlFebJOb@t`Uv^qsPa$Ig&X z#k(Thc0(60?=YT3m)8rtJHI4C?2NY*H(UDX-SQn zM7*i{>v_cwYtI!;$G#@`X=DO6m9b4z+3pJ`!D_>;&QN*SY+qd*+oX=hMf>X7mB4OY9OhGyd(?U^S?$8@9C14QwJqMD3v`J}QTjl*B1vbF<6 z>7=G6bz*AVDNRktJi$&3^_@uITUAY4&AKWV{yV*7YyaZH@BP;cU!!ch@ZYP3m1DuI zcy`HqD3|=KZrpas&*>GZITk!Ark3DppVu>3Yg74)l0naWcmu^IhA=xZ z7XClt-aWjDYU=}@CMgyOHA*SHA?f{|2~ePDg+U4yEEo`>9*YL82#O>~(W2F+TD5A` zBBi@W5u@S}6}2i7^q>xk%Bxk|%JHD$F>p{(QL6_O6wUWrbLs4f^*!JF&-dkdc4qCh z*IN6w_S)Ckb3I=7#@R14x5GI2(l;-|-u`|8Cj6&k)yb@eabF`ut z{cR~;lcpSA=(;q0U#f)70JWgZ6|w1LBQpc6CMU2!d-}{2ht6zP6Zup%o^33Bur&qS z4r&Z$-3TV=S$m1Pj)d$uRyoDT7``<@P+n!C&k4eFn%Rtn-ig;>eNby|y z1u>Rl13i8lhBFvsxD3>YZPh>}%c3sStP}y(I6NJ1ncRY=oh*#gh@vb2-a$@CAIE|l z@Jg2RT`W=luj-I@JE?<&Ef*yhQ5y?3Nl6=&i%!?tTNE>it(t)f^`J_tHcY*nFm@Lb zm~a!xZdQu&YECjKMM!Q)&qEDjZ%j>znI65)nGjEFnW4%? zbRzW9Bw^^~H0XmZ=A;?{hf*#!#HufNg_>VT$BL@FSeNTplvAIc2bLHNuv)j^B@Fkn zbSzKbo$X{>4v%)SzQTemg7Eq7$u#@jh2f;;VtyRwTyTi!@d zvm{Hn$yA|_nlwplXJz_m^e&a{uV5%xwL85JrHw042Ncd?7J@kBtfX_K_R!w9iCV&? zsVcR3;?%@;R;7>L_HO!8^ge5J8nga+uj7)-u$QL{X^TQ}?<*se>;HtSp68Nu)eRl& zMo*V4+@N#0&Ep#O&}F16-#UvzUA@%wq}v9jw5{dk6Y?T@E$480^+~D1MvXFhh5aEzGG^up#R<%wiz(3I>u?OX^GLdF+mZCmSbKk1 zWA7ZkU+5g(|Au(eBRspZVY{oOs0LNJNl%%%cxyKC4D#`Gtf2RknvGN7F@+Qji<-gm z-RVgz_|Rw!ZGOSfCRy05M-Zhe?4joL!cf<57+jq!JfRaQsbK%SGd-U6!1=Mtwa zv@eq0h5FrUBI@7YwY_3Ws#3p0GH4Kk+j>p)t5l#M?0S`BfLX%5ISCo1sB(E=`lg*c zy;0?f8C2!38G++j_dA7gtQzkGhPp>)bf9>5=_1Xm*1{VG7y1wE@q~6KXM~Z4H#G)* zdq+#N(8V*zr{`j2W+L{#cf5c(raLX;7-l}WG+)VX^lm3>>`b2$3S?v)!jQK|Pmoy? zg}o;E$_i#q1{6}p)%=RoD24Ry;~7x(n6$o!yCQ<;0Y?KjyD`FpPHLWq(!Li==Db7Kqtz|k^nyMe{Q@11A_CW57NTUJwpdEXY7t z9zcCi0NgTTvlcDgDfE;dz&(^#WA>)=q1H3XZB<{;TM+Nhp7d@@JN zBzi3=?vf0;nhal*P)o>M^MO-&21Nl{mt>@c0!uRDU<8M2Tv7csR%ZXB)HrsbmS&Vf z@9_bP0mZYOKP!DTY}J(+*iILcbV9QZl_x9dfK7Q23qsxBr`ke$ugdUX6oAiUsFKa9 z;be<{fyz2B%qJD!DwlT{hJ2eZwCGitQs zBsaj;vO~i&s&VS_Wk9

V?#7TMqgtbszYxXUqJz%0TI%>=0Ox*&w!SXN6siXEZ0SH`67(GdU|2J8GQeUHQ06P8@(eb=}84Nd`c8N>=D3l^sOcFbave=6ZfLnh8%7C@xCy-qW!6|(t z9g;3%j6DKGUmJRLUcDE>VUJa^j{(dawemw*d40mey)ew^d6)n=nC)8ZB;;Wg})uuKMF{y2(_X0 znF(6zl2A^pCe{W)Ge+DiegiQ+N2r>3Hn|Iq{61)g-;&-(iSH=lYyg_zA}q#Ixs}q< zH_|5{m5O3D?uJlq8Z;^kAsl!K)W{C_v;A<%r_u8Uda6DZvG2V88^np-jMGqmmV>*= z!cK?Ttl0h%D2X@C{Ybnnnb$$yceIinq_BYHc%ms@P`Y3oKss0eIu*mwwCRvKyx^gd z|ABNs8jhti9schHd97@r%uMBRXHkBjo*@YOWF9=*tE|#}gq0baSgnATx`U2=#)`Ka zp=}`|RI0>S&jpBRaK@4a`Qk__&3)zXR_$VYiXNrk6$n)+ptGMa-IwlcbNMusF)fonW>IqZXfX#kK}>H zxL|g)CRq~lotjQvI&9fB7=2PK?2;~lvl=6>!V=W8$pVNNzm#Volc4(I%V0{M)%wF*tN;Nw5_v!i77xqYUIGSvJQQ4c*j_Xp z)qE8d`-ARJ7+m_;dybod>fdvS**}OkP&~h%yiFbiZhZv2Uj*RuTj^(sE~zg;!Sn^^ zGaj1eL&{ZT0u9twSW8>fa|qXUKx)j>%Ii7$i~3sKFm5w`FtebfPjs$$kW2Rf`G7km z;Mu)+`37sd4%0vCV12d;CgdkINKofeYjU($`6kV7`I-!dzYHPmcCm}oNU?#NKLD^xo4Gg)9;#yMJ!|WiN?Hx~Ot;bjR5m01(E@X&Lirpnqz8li2LD2DZ!3H$0 z)NjZ`v}TJY7UTcJ`IWpUoDg%6{|!^B=`VrArrEA9KKT-x6wYAp*EJL)sxkpeGrO%V z!WVS5??4D2isH`wP{BRwG2g$?UT(jHBy=l;*1PSGu=nXp`#Z387r|SWcd9yxP6NnM z+JeC}5IFB}`U27313}6}sF@yzPI{rU+<6IGv$mqNz8m??AyiDigWtOdQA&BYs(WjL zhUglMr035R&W?KV$-}a@g0=j--_rU_BaY8QKVB75AYaGhK^f zGuD29`%Bt2tv(a>4aCNV2@v%?se8|WI9vs_=Ue&=@D{6#mqE5yG3%QhOcC_)DAcs3 zLHD@~pqhrC%Vq_urq$AtmK4fU?gE9WBCWfdE!Ro$?K&O zd7*Mj`AY2$ad(M!QF|X!t~0vf8Qp>5S3?YT0f|*(vy<5y$<;&1T-MU%#wV~9Q9+y_ z70{Y(Mgm*+Q^b+vs@}z1gq-eO>nDVu1bZ@e&FzAMzMPZhJmAa#Gx#|2rbAek3Cx{W zMG+ZbZvru-5QOx>VLSQ+SXVPNg2)QIzu$f>LgzTg#?z-+K9TPeU zYJvsoIYiAfVA~c@r|0D-F#3EdzZqK)Zv(G42)K0;RDtct-Q-^E+dBz`?>I;jj=?Ur zYJxom0)4303lf+pb%n4Ikzt(_lVtfUc<-8eM{LJ*^`815{T@9FRNOplINXjp@Ih3` zsu}f+CPvWc2tm_qO!hWoSP4Snd8Eko%{Nf9RH66k#}q0b13JWE;c!I>zFXFt8xxHs zsB`{g=#ZXmwZ_?7slzEjud!-si|xcVKnS%Uzm(LjU_|bXeDy6^<5h(~w1LqK+h=aa zT}K(?A*7m)RNxiVDBcD&REToWx0wBM<}m2s=w_t3Ft-g%%l{5w^mowqT{N#j5m41i zv>JdZqPbjG5bv)->-IHj#lDMq&HeX8E5f6w<^#x@B|OwHSf24u!S_L{^fVRFITuPJbs8I5``|mptr=DsT>?{0MfN zsevRn4J3Rf@ZBs>xe;taD)8LT-EWbsJcQu_;V?46OGu=W#8god)37Ht6F6~}n1hPZHq}TG#JDj5SFeK=3w%5KqUZh;F1O=|hG<{&F9Ah&+PGH5VGZWpI=)LnQDzw0OI) zLxXM?`wI5{BdV1*uym>;8EY!F!;V!`x{bNdejsN?0@FVPdS-_7B$DjK(hJhdz?*B2|Fm>tn}RYqgUqt2XxH+zFBFUEpmVP$$8A z&qCVuEC{w2z+G=tUxx_seRV(BoMWg6{fON65^B&qKPlMtcR-qqnegW*i2Nk2VG|Y~(<_jUmQJ>>7Ir zdt+u9a}hh1!HvIy3h!H}5bg)#dkmcKX~R2b{A%1lUCQ6r*Ajt6HeE=g`(eG0F(*K^ zFw1-fjQlFb+iXYbya$QYL2x%GuqU2sUvEHGS?fi8Uxp&nE71SH3!?cGBvN04Z2k$mAb*2|xUy3dq+t`MCCF!i`N%F#Px#3pP{rN{ zRqPCBj`OUu*m(go^hRvXe+#<+_hF0e7YIV$Y3w!o1$($ExYgWR$ikYqE!}otsWZS* zXQA+xi%cdT$iE2LE)}D5d9|1rJC094;gILMq69_tEwiy9cL)D2G!Z4(;m8R|C_Vij zEYrS#&zY!fW`pv}`*WS*fi2iTkV+(y2KI9Rkjre6OSP)`wHV0$8e1lDq(Ae5OCU)~ zhQb5K`*XALkxwH+K&lrRewEx4hkqtSK0HfSR8C9e(#vyP%1t=eHElgH7{Q+zBU zx0ZU>SP37pR)`9lu&;ib5P9<)NsmlPloIMBfcHy=jy+XwAqSym=aJ7?k#bi_?MOyj zsZxZGlZ;5-RgxNM9F*1u4}icu3_9z$Rf1}A3>*K@f{a9NFCtI^dX$X)g{e5NJTg5_ zY8F3RIe@hBFqA6Cl}K)RseQ|ARBLh}49o*4%GY*i1z_^ECuhE$6CJwJKYM9DIb7GECw!21G621cs~OpJs9{fQyk5x zrP=U|^Tb?H15{4~XBGlV%z*rOaAf37(lheZIg(g82RX+|DKGNvIa04&TEuND)$^u- z2*?I!n1f_47oyLl@=8dT*2+;7i3^||2p}uTz;=eE+D;&;IJ7z)fqoHoW*pZ!)aOIs zqH+=1i;e3>Ds~}+!BXd&hs`V49-0PPJqObGB8=cAi?fp<47aeyF%vWl-5MUXx7D`~ zAV`*iE2I%8>TH5KW1F)Bs=-Lr>mHoznZs_r5DFu?u;pS4#AGTUN*J}me2|k@)D$gE8x7Ogq!j@rC+ikA zz0B65Ao)u5Btt_XD-7;np0U=*k90au8hIgXozBAUj%@HhIT+u)sAwH9iy&MqK}9^y z3Rv-00zgVSlz_t_yezhlLy%R9ttVG7u$&X;1W=YvK^ULyobF##xo2a3qRdZ9#YF1%AdQ%IGFcsLg7$g~q_STUJM)3f27@*vtTAVuI; zO3RVRv|mZb>i=b#MCK++9U_@mNoHh3d8tyxDDstp$jvHJn@HD6QleLYJf#o{r@woU zQmle2)^)6eI6Gj++X;3mRz%Ql4+))w-M^NdhT}NGSRGkNFs9klv2|j$Jr65oEhx8q zER`br6n0IN+VR*k(EE8@S_v zi4tG!Z{9TW+ZEF2Pam4#-SQ)Y^>KPOyhRj6w<45@uITYbD)u;pVB}dy3YH@OD*%rh zL=8*Bu_ImG5E@E#0a1u%^4)DXOeIEZ2A~=e>3$jrc;XSNlK5oc%3IDL4Ph(9r^7n> zLpLy-&k4eCY0LdZ9YbOr5aMAZNkTFf_29o2bL6?}@crwq6ESk{4f1F_HRM|!G}8Yj zyeAyz@JLcysV<8k6*Gh@lBFYdp^=G%dSc31zSodpl0nAtA$PLmybvVgG(R2sc z`XXs3G!1(pB|U%vKCBnREtlw#)K=2q$j-AQu;e=F+ZCf%RGZ}#*?<5!%m5boI_i?z ztTgHSz_o19;=}-!cLI=iK#rGhO>!iwqSWkf4wg2J`q3-?K{2JZS@g#CD(IHud2Ehl zEahRb7#qh+Af$?kXT`|vzY|B}_#~!$&moJj$~KW5$Y+XB4~-%9~63cim zSgW%*5I0E*N*0{RQ)h>8OyXb-(>f0QPtBM7dA6F&f9;U&l~QxqOM z{|3XEXM^vHDmxM4PhpE`yqb!{GlU9S7HF%b>ZZl6bfi&M65HF?9|Y-eITMBUFshYV zkW)^>7J|t2>!hQR3dgqtb^ia(Kg6u-h)aGqZjGdG>nX7I$n+cJsmO?%BorCnPD+W( z#kz_lohK63{WgaJylI%(Jgk{QL^%fTa+6r4Ee@+iGWsKA4mUEPB@1ICdm(DeMOb;o zMr7W3(jaoVio_?B+Srnum&BBC`KWJ1{^M?Lvu6iui9Elw9>$J-Sr z09Q^o;k1A=r>Qpu_5GnDS_&Uo2xLR+oWM0{by;UgIeh;1Y82wqz_eE`jJQ`Z*Zr}uUrv2w~R|#1e2sOR|v?`5k;~qraMc7WB#`24dnFgh@72B0U?> zk1%VYOXn=Jmn$gT#g|!3N@N8-;vM)CE1bpIKR!~32S!K&Zcf4O^$fc|OlSDt&;dmT zi*RDpO5cDO?mPP@?xPqXiQ&FODACchB7>$BSb%YC0VYcYECwA0p&EEItBxv*UEw5jw|s$gC3*9 z_5bRyms+2-jP@vuB6$?k!)26x05{+=>vO5Nco1UYLWG6@^7#!BiIcUHd;P*VoF2o#jsSW(z zA`*v?WrUG6M*+QJ$geoQ0lQ!j8Ac8=3of$(>kVxjms@r%H1gIQ<2iTi?@?qZTu}oy zjR9mJK{W?EyGuMAL{Ww3O8uM1;D|#= zZ@AQk{?G$laTyI+M+%Xm#=zGHkzjC{{@=q$lepZ5?B<1lXfY*(oHP#k^WCs`@%=Cmh;2ufU@e{njL zT#l^&?R<&E* z{&o5Bjr=)BPjZlLa+!_%Imb|Pxs81L!Ey{D*$-RU7MJJ$J&Fj-75O)(BNkM0AT%pc zY3~Dc(OO}Tx&dl(a1xEU6ubmrbU}cdTRjQTRgln_-7rL#TbloS4qa{;joHnke_L)` zW@82l!q|}#gPl-Ir&E-pME~Y1!XOB6v#e2N7l2cV!G(v^Oe~+W8^A!gqQe}?W)N#UiXqQIrJpNp%ne3hrYYoqA?ip4P%DvsH%N!b{AyBT?<`XB zOHkBYhioySj9zQcxCNS$F=WU%hH*&IC*ur{>EIc1kwh#7%HIT>zYVFxPI3Ur#9;;+ z&*CsRA29lGF&TjIFQ}M~OrSqt;&7Bmvq0ob^PrKLC*@*?5oO6XN%_(?X@|5Ea+xAj zPm7T%oJF#52`5Zlhv*}rj7n_JNJLGxfce9~`8g~I<{^p5m$yL}u@`y;3OSRNAk?iP z78o-jo6UmKH5)SWT%;9CL5{=}?}`!t5)FbAk>Itu;!L6b>R{v+QOJX+7LlIU_E!W= z)&@f`H4Rp~l+lZbo@K$a7@SfNb-569=$U1qa~d+k7_dtKsO2vNwv_Gppv+5&*h zE);Z41JAS+0b?z=ylnux6mV&%!Vb1F!RcjLlL2vytr*yh0MJP)SPY&q7%(5c2{XU2 zM(u+cDu|mVf2|=n=@9rV>e#r9CjJ7Z5yg-T3@)nz-=|(6h6oV_0TDyS%@s6Z zW=t1zA^c?y;vscMw6QUZgO=g1`-f>9rje$^|Be^t!(v!s)=ybp91X(yhr*!$fdHI# z4jgg-v63#iC?ZFchBe%h#>p5sLoOkebv_6(hAz@5iOTe|LF%+qQ(5DvtBb;_sJAG@ zq=k@5pz+Z4@u_UsZ}p0ODJ_)^dl)e^2zU`QVixOZM%nviXyVW2X8H}d)z~1SOc0hx zzsqI$*AIgniz4cT0du%)|2jdz3z8go`4|}CAPuOw{vYU2hTvdxFqE_rcpxcjP!!Q3 zhDr4=gzo42y({Dc@QC!2bZqEfbEuVo`|)pH2q(|n8jZO{WjDuE$2{CP3h)u4zOcl< z*sXbt!j5u)0O)X0m&BEp4LMvJ4kKVuAP6VUYyn$NLvVLF;3NwX0_bQJ(&(Ge)OYBJ zEHMh!kbZHgP5seD9QJ}{8jK|5V>`a7?;jBf=zMc&{s-y2(xD9@Bn4rlTt?XinDrMJ zVMK`_uE1rMy^pz$p^^03VLIGdO}SJI1|1_h+5t5c732a92JHDg%Wmo$6YW`UQ{R~W z*0WTF4k@mX=l?CnjI4l7T_HkH6h_JA`#p^T=OS5If(2b7H0C40Ad?jYH+3gm*{8&X~bZ?#!a|pY&e7L-6-rn<}dq% zW=sn~KVOIu4S`pojSXYi!(hb22$o#r;bf_1MlvpPt50DVTp`7JY0cQ6h08!|7*6-r z46{CDG-G2Hg_Urb%@}DFgJ*~$_)!9(Yk)2fJ4r(= zCBy8G6!_oh2Je5&H-s76kj;uNG+``;a`TE!ivmGV$P(tcYgvl8)QO_{Qi!TbjMY>) z)Kd}|3pp4X?rDsg^=Mv`51y%jRaUryW?VLgJ{hGMjqTjZWFXkAneErU6GUh4rijOHZr)d$A2A89P}tT4spsZz$D- zl&qN_Xqb;;D;w_CHyi#=bxHy(pZ}pUx!9e=jCQ5p%M& z7Dt?P2P{ z5?U}b46|D9el2Wy-9hiI7(WR2$iYsKK`xV#ofO5IJ7e}e#KKZsEZAU66w zq9t=sh4AV0`!S>^F=Xj68fr^hvXKiS_;4Am{NJf7%525%NvGBe0uM*2L&<98I}YmU z%5ogmgX~u91_Ai&LYgJQUFIOOppD>i{qID97wAf-H;vM1%DeSGdl#NQjG4##`TqON zGx({DXES3c@NZHGL(4&YFGSuP0_dVW;fnk(h2eU_6tp1tFk2|`ze6zt-bF$aK=Kg+ z%)pIG{lCQkOla>5%P>?S&5^m*euprUImFAujHStR1n7migw|{nVwl4yLIQm+Mo~>_ zYc^z|vM|hyly42wgkfUp0*o=pRBttb-iXa_T5A@>Vi*H@O)kT~evrkJm=yEqjVJ`l z^jj4yOHV@ReGanw0L`AV{4YiS2Frq2hFrFPacaEm8m0cfDc*_FAcUZtTGqfQ&`toc zhe33N z;jqF8xOhL;zi!ln=i^G-vL5}--0+EZevk0A7+nPk?U>a@=`w@O;^aE`}+%;M@7vMrXoW*ncn0|2!Q%j}AL+aSn{Fkb0~P|Buwt>Dt>ktUJ1&!Ma`B2h}N$Ne`2FDO&~t0pDIE0hCB z0VzQPVuVglUVGL+#1o8MSPIHH^smUQC|iZU=lg#TKx-3_LP{9hI&uJ!!&V_hn1FQc z%e-%nSV%!H5<&Xis1t?;)BkQEAaN8KOQ90*z$ND3p$#GWXN*pJW>R%)W<5wu)2C%RY7IDA``3>MY_=^RkKtKq|IpARaYGMpayn}Bx zd;s3tqbx}IVXz`5kPSzsh+DVNgm3}AhQMpfz$n5i^8LhjCR56g+JQv3*F2zxjdD!0> zg=!|>+Gg#r{!67W@<1h2Dh`!LH9EhgbbALX;>bhM-xqQeI%QNoH2}UN7e-mGJ!B7( zs9_gcT4d@C@(isX_NwC-(WpVDL)_CJXFz0Xqa)rmQZJJGGr`5_|0Y(H>Mr>mzZ|MB z)vXw31dRAd@89u}@91C4pU3~2(f@U(|GS6&?{WISi&fCN#HvzVd$bkC-fv13OfRc^ z`yIU1J-=(G{w#Lf8p#%@leLfZE+wraxuv94q^J}n-%|P;e}S~B$ST;885N||8fhI- z0;Zj1Rp-dLR#H=M9@8oufHdUrUvv|8>6Jpqb_Hkka{?`Q#s7=EC-dLRm$cY*psWT$ zOJ0AK#goMpz%(wmBf=ENZN|~9<*@JV2^_k%o_yeu`gpX;3`oWITRoxp!zc1OGAC0_ zZX;uxy>_=g1G{tZett)0;<`8k$0jr+I<{CJBj@orgZKlsRP=y4 z@`ir5IRl4GltcFGe|HxS>G~Wh*rV33R&yL()x`B|q+cD``domG1oeoF=Q0eyCV)?* z)$(b%rE=40#MYG+Ty%GL3_jV3qpRdI{v6*F z4cddxkHJ~+&*D&k?NBaO!M2BZzyoE! z1NW?Y0?#r&YGe&GXs&5F(DIYOBXkqZX9HKQ+Jb#630x<>(CRX9dsHv*ZWvSBN}%&! z+Uf!qTP_9O?Ne+&5qQWFgM&VJ^_e8_D7MeQ^WI7gcLF!5CK@dTUbW#1JVExUaiYma z;0BB5rmXeAbKE0=r%k*xTMb-P`xkioM5D!43t8Z)&r>aLTFL@fu`UIk###^DtELY; zN{G|e*LEUs4d_o>ZD0rhgF8SUT(TL0N;U@ra7{WJcmj4TC>;er_&<$WO+>8%fvqUu i5Iazq2ROSg15yuMQYpZ|1)Ko|FToHnn4Wk~>L{ diff --git a/third-party-libs/FragmentFramework/version.txt b/third-party-libs/FragmentFramework/version.txt deleted file mode 100644 index 01e994d..0000000 --- a/third-party-libs/FragmentFramework/version.txt +++ /dev/null @@ -1 +0,0 @@ -v0.4.0 \ No newline at end of file diff --git a/widgets/SM2OBJApp - Copy.cpp b/widgets/SM2OBJApp - Copy.cpp deleted file mode 100644 index 5a330c6..0000000 --- a/widgets/SM2OBJApp - Copy.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "SM2OBJApp.h" - -//(*AppHeaders -#include "SM2OBJMain.h" -#include -//*) - -IMPLEMENT_APP(SM2OBJApp); - -bool SM2OBJApp::OnInit() -{ - //(*AppInitialize - bool wxsOK = true; - wxInitAllImageHandlers(); - if ( wxsOK ) - { - SM2OBJFrame* Frame = new SM2OBJFrame(0); - Frame->Show(); - SetTopWindow(Frame); - } - //*) - return wxsOK; - -} diff --git a/widgets/SM2OBJApp - Copy.h b/widgets/SM2OBJApp - Copy.h deleted file mode 100644 index d924c8d..0000000 --- a/widgets/SM2OBJApp - Copy.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef WIDGETTESTAPP_H -#define WIDGETTESTAPP_H - -#include - -class SM2OBJApp : public wxApp -{ - public: - virtual bool OnInit(); -}; - -#endif // WIDGETTESTAPP_H diff --git a/widgets/SM2OBJApp.cpp b/widgets/SM2OBJApp.cpp deleted file mode 100644 index 5a330c6..0000000 --- a/widgets/SM2OBJApp.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "SM2OBJApp.h" - -//(*AppHeaders -#include "SM2OBJMain.h" -#include -//*) - -IMPLEMENT_APP(SM2OBJApp); - -bool SM2OBJApp::OnInit() -{ - //(*AppInitialize - bool wxsOK = true; - wxInitAllImageHandlers(); - if ( wxsOK ) - { - SM2OBJFrame* Frame = new SM2OBJFrame(0); - Frame->Show(); - SetTopWindow(Frame); - } - //*) - return wxsOK; - -} diff --git a/widgets/SM2OBJApp.h b/widgets/SM2OBJApp.h deleted file mode 100644 index d924c8d..0000000 --- a/widgets/SM2OBJApp.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef WIDGETTESTAPP_H -#define WIDGETTESTAPP_H - -#include - -class SM2OBJApp : public wxApp -{ - public: - virtual bool OnInit(); -}; - -#endif // WIDGETTESTAPP_H diff --git a/widgets/SM2OBJMain - Copy.cpp b/widgets/SM2OBJMain - Copy.cpp deleted file mode 100644 index 7db7fa4..0000000 --- a/widgets/SM2OBJMain - Copy.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include "SM2OBJMain.h" -#include -#include -#include - -//(*InternalHeaders(SM2OBJFrame) -#include -#include -#include -#include -//*) - -#undef M_PI -#include "../source/config.h" - -void* runExporter(void* DATA); -static ffw::thread mainThread; -static SM2OBJFrame *FramePtr = NULL; -static void exportProgressCallback(int Value, int Range); -static void exportExitCallback(bool Success); - -//helper functions -enum wxbuildinfoformat { - short_f, long_f }; - -wxString wxbuildinfo(wxbuildinfoformat format) -{ - wxString wxbuild(wxVERSION_STRING); - - if (format == long_f ) - { -#if defined(__WXMSW__) - wxbuild << _T("-Windows"); -#elif defined(__UNIX__) - wxbuild << _T("-Linux"); -#endif - -#if wxUSE_UNICODE - wxbuild << _T("-Unicode build"); -#else - wxbuild << _T("-ANSI build"); -#endif // wxUSE_UNICODE - } - - return wxbuild; -} - -//(*IdInit(SM2OBJFrame) -const long SM2OBJFrame::ID_StaticText3 = wxNewId(); -const long SM2OBJFrame::ID_StaticText7 = wxNewId(); -const long SM2OBJFrame::ID_StaticText9 = wxNewId(); -const long SM2OBJFrame::ID_StaticText8 = wxNewId(); -const long SM2OBJFrame::ID_StaticText4 = wxNewId(); -const long SM2OBJFrame::ID_StaticText1 = wxNewId(); -const long SM2OBJFrame::ID_StaticText2 = wxNewId(); -const long SM2OBJFrame::ID_GetInputFilePath = wxNewId(); -const long SM2OBJFrame::ID_GetDataFolderPath = wxNewId(); -const long SM2OBJFrame::ID_GetOutputFolderPath = wxNewId(); -const long SM2OBJFrame::ID_InputFilePath = wxNewId(); -const long SM2OBJFrame::ID_InputDataPath = wxNewId(); -const long SM2OBJFrame::ID_OutputFolderPath = wxNewId(); -const long SM2OBJFrame::ID_OutputName = wxNewId(); -const long SM2OBJFrame::ID_UseDiffuse = wxNewId(); -const long SM2OBJFrame::ID_UseBump = wxNewId(); -const long SM2OBJFrame::ID_UseAlpha = wxNewId(); -const long SM2OBJFrame::ID_ExportMaterials = wxNewId(); -const long SM2OBJFrame::ID_CHECKBOX1 = wxNewId(); -const long SM2OBJFrame::ID_Panel1 = wxNewId(); -const long SM2OBJFrame::ID_StaticText5 = wxNewId(); -const long SM2OBJFrame::ID_NumOfThreads = wxNewId(); -const long SM2OBJFrame::ID_Panel3 = wxNewId(); -const long SM2OBJFrame::ID_ExportTextures = wxNewId(); -const long SM2OBJFrame::ID_TextureTga = wxNewId(); -const long SM2OBJFrame::ID_TextureBmp = wxNewId(); -const long SM2OBJFrame::ID_TextureTiff = wxNewId(); -const long SM2OBJFrame::ID_TexturePng = wxNewId(); -const long SM2OBJFrame::ID_StaticText6 = wxNewId(); -const long SM2OBJFrame::ID_CHECKBOX2 = wxNewId(); -const long SM2OBJFrame::ID_Panel2 = wxNewId(); -const long SM2OBJFrame::ID_ProgressBar = wxNewId(); -const long SM2OBJFrame::ID_StartExport = wxNewId(); -const long SM2OBJFrame::ID_MENUITEM1 = wxNewId(); -const long SM2OBJFrame::idMenuAbout = wxNewId(); -//*) - -BEGIN_EVENT_TABLE(SM2OBJFrame,wxFrame) - //(*EventTable(SM2OBJFrame) - //*) -END_EVENT_TABLE() - -SM2OBJFrame::SM2OBJFrame(wxWindow* parent,wxWindowID id) -{ - //(*Initialize(SM2OBJFrame) - wxMenuItem* MenuItem2; - wxMenuItem* MenuItem1; - wxMenu* Menu1; - wxMenuBar* MenuBar1; - wxMenu* Menu2; - - Create(parent, wxID_ANY, _("SM2OBJ"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("wxID_ANY")); - SetClientSize(wxSize(535,455)); - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - wxFont thisFont(10,wxSWISS,wxFONTSTYLE_NORMAL,wxNORMAL,false,_T("Arial"),wxFONTENCODING_DEFAULT); - SetFont(thisFont); - StaticText3 = new wxStaticText(this, ID_StaticText3, _("Material options:"), wxPoint(8,232), wxDefaultSize, 0, _T("ID_StaticText3")); - StaticText7 = new wxStaticText(this, ID_StaticText7, _("Select blueprint file"), wxPoint(8,8), wxDefaultSize, 0, _T("ID_StaticText7")); - StaticText9 = new wxStaticText(this, ID_StaticText9, _("Select output folder"), wxPoint(8,120), wxDefaultSize, 0, _T("ID_StaticText9")); - StaticText8 = new wxStaticText(this, ID_StaticText8, _("Select StarMade\\data folder (this folder must contains \'config\' and \'textures\' sub-folders)"), wxPoint(8,64), wxDefaultSize, 0, _T("ID_StaticText8")); - StaticText4 = new wxStaticText(this, ID_StaticText4, _("Name of the exporter OBJ:"), wxPoint(8,176), wxDefaultSize, 0, _T("ID_StaticText4")); - StaticText1 = new wxStaticText(this, ID_StaticText1, _("Another options:"), wxPoint(184,232), wxDefaultSize, 0, _T("ID_StaticText1")); - StaticText2 = new wxStaticText(this, ID_StaticText2, _("Texture options:"), wxPoint(360,232), wxDefaultSize, 0, _T("ID_StaticText2")); - - - GetInputFilePath = new wxButton(this, ID_GetInputFilePath, _("..."), wxPoint(496,32), wxSize(32,26), 0, wxDefaultValidator, _T("ID_GetInputFilePath")); - GetDataFolderPath = new wxButton(this, ID_GetDataFolderPath, _("..."), wxPoint(496,88), wxSize(32,26), 0, wxDefaultValidator, _T("ID_GetDataFolderPath")); - GetOutputFolderPath = new wxButton(this, ID_GetOutputFolderPath, _("..."), wxPoint(496,144), wxSize(32,26), 0, wxDefaultValidator, _T("ID_GetOutputFolderPath")); - InputFilePath = new wxTextCtrl(this, ID_InputFilePath, wxEmptyString, wxPoint(8,32), wxSize(480,24), 0, wxDefaultValidator, _T("ID_InputFilePath")); - InputDataPath = new wxTextCtrl(this, ID_InputDataPath, wxEmptyString, wxPoint(8,88), wxSize(480,24), 0, wxDefaultValidator, _T("ID_InputDataPath")); - OutputFolderPath = new wxTextCtrl(this, ID_OutputFolderPath, wxEmptyString, wxPoint(8,144), wxSize(480,24), 0, wxDefaultValidator, _T("ID_OutputFolderPath")); - OutputName = new wxTextCtrl(this, ID_OutputName, _("Blueprint"), wxPoint(8,200), wxSize(520,24), 0, wxDefaultValidator, _T("ID_OutputName")); - Panel1 = new wxPanel(this, ID_Panel1, wxPoint(8,256), wxSize(168,136), wxTAB_TRAVERSAL, _T("ID_Panel1")); - Panel1->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENU)); - UseDiffuse = new wxCheckBox(Panel1, ID_UseDiffuse, _("Use diffuse texture"), wxPoint(8,32), wxDefaultSize, 0, wxDefaultValidator, _T("ID_UseDiffuse")); - UseDiffuse->SetValue(true); - UseBump = new wxCheckBox(Panel1, ID_UseBump, _("Use bump texture"), wxPoint(8,56), wxDefaultSize, 0, wxDefaultValidator, _T("ID_UseBump")); - UseBump->SetValue(true); - UseAlpha = new wxCheckBox(Panel1, ID_UseAlpha, _("Use alpha texture"), wxPoint(8,80), wxDefaultSize, 0, wxDefaultValidator, _T("ID_UseAlpha")); - UseAlpha->SetValue(true); - ExportMaterials = new wxCheckBox(Panel1, ID_ExportMaterials, _("Export materials"), wxPoint(8,8), wxDefaultSize, 0, wxDefaultValidator, _T("ID_ExportMaterials")); - ExportMaterials->SetValue(true); - SpecularHighlight = new wxCheckBox(Panel1, ID_CHECKBOX1, _("Specular highlight"), wxPoint(8,104), wxDefaultSize, 0, wxDefaultValidator, _T("ID_SpecularHighlight")); - SpecularHighlight->SetValue(true); - Panel3 = new wxPanel(this, ID_Panel3, wxPoint(184,256), wxSize(168,136), wxTAB_TRAVERSAL, _T("ID_Panel3")); - Panel3->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENU)); - StaticText5 = new wxStaticText(Panel3, ID_StaticText5, _("Thread count:"), wxPoint(8,8), wxDefaultSize, 0, _T("ID_StaticText5")); - NumOfThreads = new wxSpinCtrl(Panel3, ID_NumOfThreads, _T("1"), wxPoint(96,8), wxSize(64,21), 0, 1, 16, 1, _T("ID_NumOfThreads")); - NumOfThreads->SetValue(_T("1")); - Panel2 = new wxPanel(this, ID_Panel2, wxPoint(360,256), wxSize(168,136), wxTAB_TRAVERSAL, _T("ID_Panel2")); - Panel2->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENU)); - ExportTextures = new wxCheckBox(Panel2, ID_ExportTextures, _("Export textures"), wxPoint(8,8), wxDefaultSize, 0, wxDefaultValidator, _T("ID_ExportTextures")); - ExportTextures->SetValue(false); - TextureTga = new wxRadioButton(Panel2, ID_TextureTga, _("TGA"), wxPoint(8,64), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TextureTga")); - TextureBmp = new wxRadioButton(Panel2, ID_TextureBmp, _("BMP"), wxPoint(8,88), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TextureBmp")); - TextureTiff = new wxRadioButton(Panel2, ID_TextureTiff, _("TIFF"), wxPoint(96,88), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TextureTiff")); - TexturePng = new wxRadioButton(Panel2, ID_TexturePng, _("PNG"), wxPoint(96,64), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TexturePng")); - TexturePng->SetValue(true); - StaticText6 = new wxStaticText(Panel2, ID_StaticText6, _("Texture format:"), wxPoint(8,40), wxDefaultSize, 0, _T("ID_StaticText6")); - ExportUVs = new wxCheckBox(Panel2, ID_CHECKBOX2, _("Export UVs"), wxPoint(8,112), wxDefaultSize, 0, wxDefaultValidator, _T("ID_ExportUVs")); - ExportUVs->SetValue(true); - ProgressBar = new wxGauge(this, ID_ProgressBar, 100, wxPoint(120,400), wxSize(408,28), 0, wxDefaultValidator, _T("ID_ProgressBar")); - StartExport = new wxButton(this, ID_StartExport, _("Start Exporting"), wxPoint(8,400), wxDefaultSize, 0, wxDefaultValidator, _T("ID_StartExport")); - MenuBar1 = new wxMenuBar(); - Menu1 = new wxMenu(); - MenuItem1 = new wxMenuItem(Menu1, ID_MENUITEM1, _("Quit\tAlt-F4"), _("Quit the application"), wxITEM_NORMAL); - Menu1->Append(MenuItem1); - MenuBar1->Append(Menu1, _("&File")); - Menu2 = new wxMenu(); - MenuItem2 = new wxMenuItem(Menu2, idMenuAbout, _("About\tF1"), _("Show info about this application"), wxITEM_NORMAL); - Menu2->Append(MenuItem2); - MenuBar1->Append(Menu2, _("Help")); - SetMenuBar(MenuBar1); - - Connect(ID_StartExport,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&SM2OBJFrame::OnStartExportClick); - Connect(ID_GetInputFilePath,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&SM2OBJFrame::OnGetInputFilePathClick); - Connect(ID_GetDataFolderPath,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&SM2OBJFrame::OnGetDataFolderPathClick); - Connect(ID_GetOutputFolderPath,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&SM2OBJFrame::OnGetOutputFolderPathClick); - Connect(ID_MENUITEM1,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&SM2OBJFrame::OnQuit); - Connect(idMenuAbout,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&SM2OBJFrame::OnAbout); - //*) -} - -SM2OBJFrame::~SM2OBJFrame(){ - //(*Destroy(SM2OBJFrame) - //*) -} - -void SM2OBJFrame::OnQuit(wxCommandEvent& event){ - Close(); -} - -void SM2OBJFrame::OnAbout(wxCommandEvent& event){ - wxString msg = "StarMade blueprint to OBJ version 1.2"; - wxMessageBox(msg, _("Welcome!")); -} - -void exportProgressCallback(int Value, int Range){ - if(FramePtr == NULL)return; - FramePtr->ProgressBar->SetValue(Value); - FramePtr->ProgressBar->SetRange(Range); -} - -void exportExitCallback(bool Success){ - if(FramePtr == NULL)return; - - if(FramePtr != NULL){ - FramePtr->StartExport->Enable(); - } - if(Success)wxMessageBox( wxT("Export completed!"), wxT("Hurray!"), wxICON_INFORMATION); - else wxMessageBox( wxT("Something went wrong. Check console output!"), wxT("Ouch!"), wxICON_ERROR); -} - -void SM2OBJFrame::OnStartExportClick(wxSpinEvent& event){ - if(TextureBmp->GetValue()){ - imageSaver = &ffw::saveBMP; - imageExtension = "bmp"; - } else if(TexturePng->GetValue()){ - imageSaver = &ffw::savePNG; - imageExtension = "png"; - } else if(TextureTga->GetValue()){ - imageSaver = &ffw::saveTGA; - imageExtension = "tga"; - } else if(TextureTiff->GetValue()){ - imageSaver = &ffw::saveTIFF; - imageExtension = "tiff"; - } - - textureExport = ExportTextures->GetValue(); - materialExport = ExportMaterials->GetValue(); - threadsCount = NumOfThreads->GetValue(); - filePath = InputFilePath->GetValue(); - fileOutputFolder = OutputFolderPath->GetValue(); - fileName = OutputName->GetValue(); - starMadeDataFolder = InputDataPath->GetValue(); - - exportDiffuse = UseDiffuse->GetValue(); - exportBump = UseBump->GetValue(); - exportAlpha = UseAlpha->GetValue(); - - exportProgressFunc = &exportProgressCallback; - exportExitFunc = &exportExitCallback; - - useSpecularHighlight = SpecularHighlight->GetValue(); - uvsExport = ExportUVs->GetValue(); - - FramePtr = this; - StartExport->Disable(); - mainThread.bindFunction(&runExporter); - mainThread.start(NULL); -} - -void SM2OBJFrame::OnGetDataFolderPathClick(wxSpinEvent& event){ - wxDirDialog dialog(this, wxT("Select StarMade data folder"), wxT(""), wxDD_DEFAULT_STYLE, wxDefaultPosition, wxDefaultSize, wxT("Select folder")); - if(dialog.ShowModal() == wxID_OK){ - InputDataPath->SetValue(dialog.GetPath()); - } -} - -void SM2OBJFrame::OnGetInputFilePathClick(wxSpinEvent& event){ - wxFileDialog openFileDialog(this, _("Select blueprint file"), "", "", "SMD2 files (*.smd2)|*.smd2", wxFD_OPEN|wxFD_FILE_MUST_EXIST); - if(openFileDialog.ShowModal() == wxID_CANCEL) return; - - InputFilePath->SetValue(openFileDialog.GetPath()); -} - -void SM2OBJFrame::OnGetOutputFolderPathClick(wxSpinEvent& event){ - wxDirDialog dialog(this, wxT("Select output folder"), wxT(""), wxDD_DEFAULT_STYLE, wxDefaultPosition, wxDefaultSize, wxT("Select folder")); - if(dialog.ShowModal() == wxID_OK){ - OutputFolderPath->SetValue(dialog.GetPath()); - } -} diff --git a/widgets/SM2OBJMain - Copy.h b/widgets/SM2OBJMain - Copy.h deleted file mode 100644 index 5e123da..0000000 --- a/widgets/SM2OBJMain - Copy.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef WIDGETTESTMAIN_H -#define WIDGETTESTMAIN_H - -//(*Headers(SM2OBJFrame) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//*) - -class SM2OBJFrame: public wxFrame -{ - public: - - SM2OBJFrame(wxWindow* parent,wxWindowID id = -1); - virtual ~SM2OBJFrame(); - - //private: - - //(*Handlers(SM2OBJFrame) - void OnQuit(wxCommandEvent& event); - void OnAbout(wxCommandEvent& event); - - void OnStartExportClick(wxSpinEvent& event); - void OnGetDataFolderPathClick(wxSpinEvent& event); - void OnGetInputFilePathClick(wxSpinEvent& event); - void OnGetOutputFolderPathClick(wxSpinEvent& event); - //*) - - //(*Identifiers(SM2OBJFrame) - static const long ID_StaticText3; - static const long ID_StaticText7; - static const long ID_StaticText9; - static const long ID_StaticText8; - static const long ID_StaticText4; - static const long ID_StaticText1; - static const long ID_StaticText2; - static const long ID_GetInputFilePath; - static const long ID_GetDataFolderPath; - static const long ID_GetOutputFolderPath; - static const long ID_InputFilePath; - static const long ID_InputDataPath; - static const long ID_OutputFolderPath; - static const long ID_OutputName; - static const long ID_UseDiffuse; - static const long ID_UseBump; - static const long ID_UseAlpha; - static const long ID_ExportMaterials; - static const long ID_CHECKBOX1; - static const long ID_Panel1; - static const long ID_StaticText5; - static const long ID_NumOfThreads; - static const long ID_Panel3; - static const long ID_ExportTextures; - static const long ID_TextureTga; - static const long ID_TextureBmp; - static const long ID_TextureTiff; - static const long ID_TexturePng; - static const long ID_StaticText6; - static const long ID_CHECKBOX2; - static const long ID_Panel2; - static const long ID_ProgressBar; - static const long ID_StartExport; - static const long ID_MENUITEM1; - static const long idMenuAbout; - //*) - - //(*Declarations(SM2OBJFrame) - wxStaticText* StaticText9; - wxTextCtrl* OutputName; - wxButton* StartExport; - wxCheckBox* UseDiffuse; - wxStaticText* StaticText2; - wxStaticText* StaticText6; - wxCheckBox* UseAlpha; - wxTextCtrl* InputFilePath; - wxStaticText* StaticText8; - wxCheckBox* ExportUVs; - wxCheckBox* UseBump; - wxPanel* Panel1; - wxTextCtrl* OutputFolderPath; - wxStaticText* StaticText1; - wxRadioButton* TextureBmp; - wxStaticText* StaticText3; - wxSpinCtrl* NumOfThreads; - wxPanel* Panel3; - wxCheckBox* SpecularHighlight; - wxRadioButton* TexturePng; - wxCheckBox* ExportMaterials; - wxStaticText* StaticText5; - wxStaticText* StaticText7; - wxButton* GetDataFolderPath; - wxRadioButton* TextureTga; - wxRadioButton* TextureTiff; - wxTextCtrl* InputDataPath; - wxPanel* Panel2; - wxButton* GetInputFilePath; - wxButton* GetOutputFolderPath; - wxStaticText* StaticText4; - wxGauge* ProgressBar; - wxCheckBox* ExportTextures; - - //*) - - DECLARE_EVENT_TABLE() -}; - -#endif // WIDGETTESTMAIN_H diff --git a/widgets/SM2OBJMain.cpp b/widgets/SM2OBJMain.cpp deleted file mode 100644 index 86d7d1a..0000000 --- a/widgets/SM2OBJMain.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#include "SM2OBJMain.h" -#include -#include -#include - -//(*InternalHeaders(SM2OBJFrame) -#include -#include -#include -#include -//*) - -#undef M_PI -#include "../source/config.h" - -void* runExporter(void* DATA); -static ffw::thread mainThread; -static SM2OBJFrame *FramePtr = NULL; -static void exportProgressCallback(int Value, int Range); -static void exportExitCallback(bool Success); - -//helper functions -enum wxbuildinfoformat { - short_f, long_f }; - -wxString wxbuildinfo(wxbuildinfoformat format) -{ - wxString wxbuild(wxVERSION_STRING); - - if (format == long_f ) - { -#if defined(__WXMSW__) - wxbuild << _T("-Windows"); -#elif defined(__UNIX__) - wxbuild << _T("-Linux"); -#endif - -#if wxUSE_UNICODE - wxbuild << _T("-Unicode build"); -#else - wxbuild << _T("-ANSI build"); -#endif // wxUSE_UNICODE - } - - return wxbuild; -} - -//(*IdInit(SM2OBJFrame) -const long SM2OBJFrame::ID_StaticText3 = wxNewId(); -const long SM2OBJFrame::ID_StaticText7 = wxNewId(); -const long SM2OBJFrame::ID_StaticText9 = wxNewId(); -const long SM2OBJFrame::ID_StaticText8 = wxNewId(); -const long SM2OBJFrame::ID_StaticText4 = wxNewId(); -const long SM2OBJFrame::ID_StaticText1 = wxNewId(); -const long SM2OBJFrame::ID_StaticText2 = wxNewId(); -const long SM2OBJFrame::ID_StaticBox1 = wxNewId(); -const long SM2OBJFrame::ID_StaticBox2 = wxNewId(); -const long SM2OBJFrame::ID_StaticBox3 = wxNewId(); -const long SM2OBJFrame::ID_StaticBox4 = wxNewId(); -const long SM2OBJFrame::ID_StaticBox5 = wxNewId(); -const long SM2OBJFrame::ID_SplitTiles = wxNewId(); -const long SM2OBJFrame::ID_GetInputFilePath = wxNewId(); -const long SM2OBJFrame::ID_GetDataFolderPath = wxNewId(); -const long SM2OBJFrame::ID_GetOutputFolderPath = wxNewId(); -const long SM2OBJFrame::ID_InputFilePath = wxNewId(); -const long SM2OBJFrame::ID_InputDataPath = wxNewId(); -const long SM2OBJFrame::ID_OutputFolderPath = wxNewId(); -const long SM2OBJFrame::ID_OutputName = wxNewId(); -const long SM2OBJFrame::ID_UseDiffuse = wxNewId(); -const long SM2OBJFrame::ID_UseBump = wxNewId(); -const long SM2OBJFrame::ID_UseAlpha = wxNewId(); -const long SM2OBJFrame::ID_ExportMaterials = wxNewId(); -const long SM2OBJFrame::ID_SpecularHighlight = wxNewId(); -const long SM2OBJFrame::ID_StaticText5 = wxNewId(); -const long SM2OBJFrame::ID_NumOfThreads = wxNewId(); -const long SM2OBJFrame::ID_ExportTextures = wxNewId(); -const long SM2OBJFrame::ID_TextureTga = wxNewId(); -const long SM2OBJFrame::ID_TextureBmp = wxNewId(); -const long SM2OBJFrame::ID_TextureTiff = wxNewId(); -const long SM2OBJFrame::ID_TexturePng = wxNewId(); -const long SM2OBJFrame::ID_StaticText6 = wxNewId(); -const long SM2OBJFrame::ID_ExportUVs = wxNewId(); -const long SM2OBJFrame::ID_ProgressBar = wxNewId(); -const long SM2OBJFrame::ID_StartExport = wxNewId(); -const long SM2OBJFrame::ID_MENUITEM1 = wxNewId(); -const long SM2OBJFrame::idMenuAbout = wxNewId(); -//*) - -BEGIN_EVENT_TABLE(SM2OBJFrame,wxFrame) - //(*EventTable(SM2OBJFrame) - //*) -END_EVENT_TABLE() - -SM2OBJFrame::SM2OBJFrame(wxWindow* parent,wxWindowID id) -{ - //(*Initialize(SM2OBJFrame) - wxMenuItem* MenuItem2; - wxMenuItem* MenuItem1; - wxMenu* Menu1; - wxMenuBar* MenuBar1; - wxMenu* Menu2; - - Create(parent, wxID_ANY, _("SM2OBJ"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("wxID_ANY")); - SetClientSize(wxSize(535,455)); - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - wxFont thisFont(10,wxSWISS,wxFONTSTYLE_NORMAL,wxNORMAL,false,_T("Arial"),wxFONTENCODING_DEFAULT); - SetFont(thisFont); - - - StaticText7 = new wxStaticText(this, ID_StaticText7, _("Select blueprint file"), wxPoint(8,8), wxDefaultSize, 0, _T("ID_StaticText7")); - StaticText9 = new wxStaticText(this, ID_StaticText9, _("Select output folder"), wxPoint(8,120), wxDefaultSize, 0, _T("ID_StaticText9")); - StaticText8 = new wxStaticText(this, ID_StaticText8, _("Select StarMade\\data folder (this folder must contains \'config\' and \'textures\' sub-folders)"), wxPoint(8,64), wxDefaultSize, 0, _T("ID_StaticText8")); - StaticText4 = new wxStaticText(this, ID_StaticText4, _("Name of the exported OBJ:"), wxPoint(8,176), wxDefaultSize, 0, _T("ID_StaticText4")); - - StaticBox2 = new wxStaticBox(this, ID_StaticBox2, _("Materials options"), wxPoint(8,232), wxSize(168,152), 0, _T("ID_StaticBox2")); - StaticBox5 = new wxStaticBox(this, ID_StaticBox2, _("Texture options"), wxPoint(360,232), wxSize(168,72), 0, _T("ID_StaticBox2")); - StaticBox3 = new wxStaticBox(this, ID_StaticBox2, _("UV options"), wxPoint(184,312), wxSize(168,72), 0, _T("ID_StaticBox2")); - StaticBox4 = new wxStaticBox(this, ID_StaticBox2, _("Texture format"), wxPoint(360,312), wxSize(168,72), 0, _T("ID_StaticBox2")); - StaticBox1 = new wxStaticBox(this, ID_StaticBox2, _("System options"), wxPoint(184,232), wxSize(168,72), 0, _T("ID_StaticBox2")); - - GetInputFilePath = new wxButton(this, ID_GetInputFilePath, _("..."), wxPoint(496,32), wxSize(32,26), 0, wxDefaultValidator, _T("ID_GetInputFilePath")); - GetDataFolderPath = new wxButton(this, ID_GetDataFolderPath, _("..."), wxPoint(496,88), wxSize(32,26), 0, wxDefaultValidator, _T("ID_GetDataFolderPath")); - GetOutputFolderPath = new wxButton(this, ID_GetOutputFolderPath, _("..."), wxPoint(496,144), wxSize(32,26), 0, wxDefaultValidator, _T("ID_GetOutputFolderPath")); - InputFilePath = new wxTextCtrl(this, ID_InputFilePath, wxEmptyString, wxPoint(8,32), wxSize(480,24), 0, wxDefaultValidator, _T("ID_InputFilePath")); - InputDataPath = new wxTextCtrl(this, ID_InputDataPath, wxEmptyString, wxPoint(8,88), wxSize(480,24), 0, wxDefaultValidator, _T("ID_InputDataPath")); - OutputFolderPath = new wxTextCtrl(this, ID_OutputFolderPath, wxEmptyString, wxPoint(8,144), wxSize(480,24), 0, wxDefaultValidator, _T("ID_OutputFolderPath")); - OutputName = new wxTextCtrl(this, ID_OutputName, _("Blueprint"), wxPoint(8,200), wxSize(520,24), 0, wxDefaultValidator, _T("ID_OutputName")); - - StartExport = new wxButton(this, ID_StartExport, _("Start Exporting"), wxPoint(8,392), wxDefaultSize, 0, wxDefaultValidator, _T("ID_StartExport")); - ProgressBar = new wxGauge(this, ID_ProgressBar, 100, wxPoint(120,392), wxSize(408,28), 0, wxDefaultValidator, _T("ID_ProgressBar")); - ExportMaterials = new wxCheckBox(this, ID_ExportMaterials, _("Export materials"), wxPoint(16,256), wxDefaultSize, 0, wxDefaultValidator, _T("ID_ExportMaterials")); - ExportMaterials->SetValue(true); - UseDiffuse = new wxCheckBox(this, ID_UseDiffuse, _("Use diffuse texture"), wxPoint(16,280), wxDefaultSize, 0, wxDefaultValidator, _T("ID_UseDiffuse")); - UseDiffuse->SetValue(true); - UseBump = new wxCheckBox(this, ID_UseBump, _("Use bump texture"), wxPoint(16,304), wxDefaultSize, 0, wxDefaultValidator, _T("ID_UseBump")); - UseBump->SetValue(true); - UseAlpha = new wxCheckBox(this, ID_UseAlpha, _("Use alpha texture"), wxPoint(16,328), wxDefaultSize, 0, wxDefaultValidator, _T("ID_UseAlpha")); - UseAlpha->SetValue(true); - SpecularHighlight = new wxCheckBox(this, ID_SpecularHighlight, _("Specular highlight"), wxPoint(16,352), wxDefaultSize, 0, wxDefaultValidator, _T("ID_SpecularHighlight")); - SpecularHighlight->SetValue(true); - ExportTextures = new wxCheckBox(this, ID_ExportTextures, _("Export textures"), wxPoint(368,256), wxDefaultSize, 0, wxDefaultValidator, _T("ID_ExportTextures")); - ExportTextures->SetValue(false); - SplitTiles = new wxCheckBox(this, ID_SplitTiles, _("Split tiles"), wxPoint(192,360), wxDefaultSize, 0, wxDefaultValidator, _T("ID_SplitTiles")); - SplitTiles->SetValue(true); - StaticText5 = new wxStaticText(this, ID_StaticText5, _("Thread count:"), wxPoint(192,256), wxDefaultSize, 0, _T("ID_StaticText5")); - NumOfThreads = new wxSpinCtrl(this, ID_NumOfThreads, _T("1"), wxPoint(280,256), wxSize(64,21), 0, 1, 16, 1, _T("ID_NumOfThreads")); - NumOfThreads->SetValue(_T("1")); - TextureTga = new wxRadioButton(this, ID_TextureTga, _("TGA"), wxPoint(368,336), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TextureTga")); - TexturePng = new wxRadioButton(this, ID_TexturePng, _("PNG"), wxPoint(448,336), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TexturePng")); - TexturePng->SetValue(true); - TextureBmp = new wxRadioButton(this, ID_TextureBmp, _("BMP"), wxPoint(368,360), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TextureBmp")); - TextureTiff = new wxRadioButton(this, ID_TextureTiff, _("TIFF"), wxPoint(448,360), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TextureTiff")); - ExportUVs = new wxCheckBox(this, ID_ExportUVs, _("Export UVs"), wxPoint(192,336), wxDefaultSize, 0, wxDefaultValidator, _T("ID_ExportUVs")); - ExportUVs->SetValue(true); - - /*UseDiffuse = new wxCheckBox(Panel1, ID_UseDiffuse, _("Use diffuse texture"), wxPoint(8,32), wxDefaultSize, 0, wxDefaultValidator, _T("ID_UseDiffuse")); - UseDiffuse->SetValue(true); - UseBump = new wxCheckBox(Panel1, ID_UseBump, _("Use bump texture"), wxPoint(8,56), wxDefaultSize, 0, wxDefaultValidator, _T("ID_UseBump")); - UseBump->SetValue(true); - UseAlpha = new wxCheckBox(Panel1, ID_UseAlpha, _("Use alpha texture"), wxPoint(8,80), wxDefaultSize, 0, wxDefaultValidator, _T("ID_UseAlpha")); - UseAlpha->SetValue(true); - ExportMaterials = new wxCheckBox(Panel1, ID_ExportMaterials, _("Export materials"), wxPoint(8,8), wxDefaultSize, 0, wxDefaultValidator, _T("ID_ExportMaterials")); - ExportMaterials->SetValue(true); - SpecularHighlight = new wxCheckBox(Panel1, ID_CHECKBOX1, _("Specular highlight"), wxPoint(8,104), wxDefaultSize, 0, wxDefaultValidator, _T("ID_SpecularHighlight")); - SpecularHighlight->SetValue(true); - StaticText5 = new wxStaticText(Panel3, ID_StaticText5, _("Thread count:"), wxPoint(8,8), wxDefaultSize, 0, _T("ID_StaticText5")); - NumOfThreads = new wxSpinCtrl(Panel3, ID_NumOfThreads, _T("1"), wxPoint(96,8), wxSize(64,21), 0, 1, 16, 1, _T("ID_NumOfThreads")); - NumOfThreads->SetValue(_T("1")); - ExportTextures = new wxCheckBox(Panel2, ID_ExportTextures, _("Export textures"), wxPoint(8,8), wxDefaultSize, 0, wxDefaultValidator, _T("ID_ExportTextures")); - ExportTextures->SetValue(false); - TextureTga = new wxRadioButton(Panel2, ID_TextureTga, _("TGA"), wxPoint(8,64), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TextureTga")); - TextureBmp = new wxRadioButton(Panel2, ID_TextureBmp, _("BMP"), wxPoint(8,88), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TextureBmp")); - TextureTiff = new wxRadioButton(Panel2, ID_TextureTiff, _("TIFF"), wxPoint(96,88), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TextureTiff")); - TexturePng = new wxRadioButton(Panel2, ID_TexturePng, _("PNG"), wxPoint(96,64), wxDefaultSize, 0, wxDefaultValidator, _T("ID_TexturePng")); - TexturePng->SetValue(true); - StaticText6 = new wxStaticText(Panel2, ID_StaticText6, _("Texture format:"), wxPoint(8,40), wxDefaultSize, 0, _T("ID_StaticText6")); - ExportUVs = new wxCheckBox(Panel2, ID_CHECKBOX2, _("Export UVs"), wxPoint(8,112), wxDefaultSize, 0, wxDefaultValidator, _T("ID_ExportUVs")); - ExportUVs->SetValue(true); - ProgressBar = new wxGauge(this, ID_ProgressBar, 100, wxPoint(120,400), wxSize(408,28), 0, wxDefaultValidator, _T("ID_ProgressBar")); - StartExport = new wxButton(this, ID_StartExport, _("Start Exporting"), wxPoint(8,400), wxDefaultSize, 0, wxDefaultValidator, _T("ID_StartExport"));*/ - - - MenuBar1 = new wxMenuBar(); - Menu1 = new wxMenu(); - MenuItem1 = new wxMenuItem(Menu1, ID_MENUITEM1, _("Quit\tAlt-F4"), _("Quit the application"), wxITEM_NORMAL); - Menu1->Append(MenuItem1); - MenuBar1->Append(Menu1, _("&File")); - Menu2 = new wxMenu(); - MenuItem2 = new wxMenuItem(Menu2, idMenuAbout, _("About\tF1"), _("Show info about this application"), wxITEM_NORMAL); - Menu2->Append(MenuItem2); - MenuBar1->Append(Menu2, _("Help")); - SetMenuBar(MenuBar1); - - Connect(ID_StartExport,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&SM2OBJFrame::OnStartExportClick); - Connect(ID_GetInputFilePath,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&SM2OBJFrame::OnGetInputFilePathClick); - Connect(ID_GetDataFolderPath,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&SM2OBJFrame::OnGetDataFolderPathClick); - Connect(ID_GetOutputFolderPath,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&SM2OBJFrame::OnGetOutputFolderPathClick); - Connect(ID_MENUITEM1,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&SM2OBJFrame::OnQuit); - Connect(idMenuAbout,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&SM2OBJFrame::OnAbout); - //*) -} - -SM2OBJFrame::~SM2OBJFrame(){ - //(*Destroy(SM2OBJFrame) - //*) -} - -void SM2OBJFrame::OnQuit(wxCommandEvent& event){ - Close(); -} - -void SM2OBJFrame::OnAbout(wxCommandEvent& event){ - wxString msg = "StarMade blueprint to OBJ version 1.2"; - wxMessageBox(msg, _("Welcome!")); -} - -void exportProgressCallback(int Value, int Range){ - if(FramePtr == NULL)return; - FramePtr->ProgressBar->SetValue(Value); - FramePtr->ProgressBar->SetRange(Range); -} - -void exportExitCallback(bool Success){ - if(FramePtr == NULL)return; - - if(FramePtr != NULL){ - FramePtr->StartExport->Enable(); - } - if(Success)wxMessageBox( wxT("Export complete!"), wxT("Hurray!"), wxICON_INFORMATION); - else wxMessageBox( wxT("Something went wrong. Check console output!"), wxT("Ouch!"), wxICON_ERROR); -} - -void SM2OBJFrame::OnStartExportClick(wxSpinEvent& event){ - if(TextureBmp->GetValue()){ - imageSaver = &ffw::saveBMP; - imageExtension = "bmp"; - } else if(TexturePng->GetValue()){ - imageSaver = &ffw::savePNG; - imageExtension = "png"; - } else if(TextureTga->GetValue()){ - imageSaver = &ffw::saveTGA; - imageExtension = "tga"; - } else if(TextureTiff->GetValue()){ - imageSaver = &ffw::saveTIFF; - imageExtension = "tiff"; - } - - textureExport = ExportTextures->GetValue(); - materialExport = ExportMaterials->GetValue(); - threadsCount = NumOfThreads->GetValue(); - filePath = InputFilePath->GetValue(); - fileOutputFolder = OutputFolderPath->GetValue(); - fileName = OutputName->GetValue(); - starMadeDataFolder = InputDataPath->GetValue(); - - exportDiffuse = UseDiffuse->GetValue(); - exportBump = UseBump->GetValue(); - exportAlpha = UseAlpha->GetValue(); - - exportProgressFunc = &exportProgressCallback; - exportExitFunc = &exportExitCallback; - - useSpecularHighlight = SpecularHighlight->GetValue(); - uvsExport = ExportUVs->GetValue(); - textureSplit = SplitTiles->GetValue(); - - FramePtr = this; - StartExport->Disable(); - mainThread.bindFunction(&runExporter); - mainThread.start(NULL); -} - -void SM2OBJFrame::OnGetDataFolderPathClick(wxSpinEvent& event){ - wxDirDialog dialog(this, wxT("Select StarMade data folder"), wxT(""), wxDD_DEFAULT_STYLE, wxDefaultPosition, wxDefaultSize, wxT("Select folder")); - if(dialog.ShowModal() == wxID_OK){ - InputDataPath->SetValue(dialog.GetPath()); - } -} - -void SM2OBJFrame::OnGetInputFilePathClick(wxSpinEvent& event){ - wxFileDialog openFileDialog(this, _("Select blueprint file"), "", "", "SMD2 files (*.smd2)|*.smd2", wxFD_OPEN|wxFD_FILE_MUST_EXIST); - if(openFileDialog.ShowModal() == wxID_CANCEL) return; - - InputFilePath->SetValue(openFileDialog.GetPath()); -} - -void SM2OBJFrame::OnGetOutputFolderPathClick(wxSpinEvent& event){ - wxDirDialog dialog(this, wxT("Select output folder"), wxT(""), wxDD_DEFAULT_STYLE, wxDefaultPosition, wxDefaultSize, wxT("Select folder")); - if(dialog.ShowModal() == wxID_OK){ - OutputFolderPath->SetValue(dialog.GetPath()); - } -} diff --git a/widgets/SM2OBJMain.h b/widgets/SM2OBJMain.h deleted file mode 100644 index fa31182..0000000 --- a/widgets/SM2OBJMain.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef WIDGETTESTMAIN_H -#define WIDGETTESTMAIN_H - -//(*Headers(SM2OBJFrame) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//*) - -class SM2OBJFrame: public wxFrame -{ - public: - - SM2OBJFrame(wxWindow* parent,wxWindowID id = -1); - virtual ~SM2OBJFrame(); - - //private: - - //(*Handlers(SM2OBJFrame) - void OnQuit(wxCommandEvent& event); - void OnAbout(wxCommandEvent& event); - - void OnStartExportClick(wxSpinEvent& event); - void OnGetDataFolderPathClick(wxSpinEvent& event); - void OnGetInputFilePathClick(wxSpinEvent& event); - void OnGetOutputFolderPathClick(wxSpinEvent& event); - //*) - - - //(*Identifiers(SM2OBJFrame) - static const long ID_StaticText3; - static const long ID_StaticText7; - static const long ID_StaticText9; - static const long ID_StaticText8; - static const long ID_StaticText4; - static const long ID_StaticText1; - static const long ID_StaticText2; - static const long ID_StaticBox1; - static const long ID_StaticBox2; - static const long ID_StaticBox3; - static const long ID_StaticBox4; - static const long ID_StaticBox5; - static const long ID_GetInputFilePath; - static const long ID_GetDataFolderPath; - static const long ID_GetOutputFolderPath; - static const long ID_InputFilePath; - static const long ID_InputDataPath; - static const long ID_OutputFolderPath; - static const long ID_OutputName; - static const long ID_UseDiffuse; - static const long ID_UseBump; - static const long ID_UseAlpha; - static const long ID_ExportMaterials; - static const long ID_ExportUVs; - static const long ID_StaticText5; - static const long ID_NumOfThreads; - static const long ID_SpecularHighlight; - static const long ID_TextureTga; - static const long ID_TextureBmp; - static const long ID_TextureTiff; - static const long ID_TexturePng; - static const long ID_StaticText6; - static const long ID_ExportTextures; - static const long ID_ProgressBar; - static const long ID_StartExport; - static const long ID_MENUITEM1; - static const long ID_SplitTiles; - static const long idMenuAbout; - //*) - - //(*Declarations(SM2OBJFrame) - wxStaticText* StaticText9; - wxTextCtrl* OutputName; - wxButton* StartExport; - wxCheckBox* UseDiffuse; - wxStaticText* StaticText2; - wxStaticText* StaticText6; - wxCheckBox* UseAlpha; - wxTextCtrl* InputFilePath; - wxStaticText* StaticText8; - wxCheckBox* ExportUVs; - wxCheckBox* UseBump; - wxTextCtrl* OutputFolderPath; - wxStaticText* StaticText1; - wxRadioButton* TextureBmp; - wxStaticText* StaticText3; - wxSpinCtrl* NumOfThreads; - wxCheckBox* SpecularHighlight; - wxRadioButton* TexturePng; - wxCheckBox* ExportMaterials; - wxStaticText* StaticText5; - wxStaticText* StaticText7; - wxButton* GetDataFolderPath; - wxRadioButton* TextureTga; - wxRadioButton* TextureTiff; - wxTextCtrl* InputDataPath; - wxButton* GetInputFilePath; - wxButton* GetOutputFolderPath; - wxStaticText* StaticText4; - wxGauge* ProgressBar; - wxCheckBox* ExportTextures; - wxCheckBox* SplitTiles; - wxStaticBox* StaticBox1; - wxStaticBox* StaticBox2; - wxStaticBox* StaticBox3; - wxStaticBox* StaticBox4; - wxStaticBox* StaticBox5; - - //*) - - DECLARE_EVENT_TABLE() -}; - -#endif // WIDGETTESTMAIN_H diff --git a/wxsmith/SM2OBJframe.wxs b/wxsmith/SM2OBJframe.wxs deleted file mode 100644 index 3a465c9..0000000 --- a/wxsmith/SM2OBJframe.wxs +++ /dev/null @@ -1,189 +0,0 @@ - - - - SM2OBJ - 539,444 - wxSYS_COLOUR_WINDOW - - 10 - - normal - 0 - swiss - Arial - - 0 - - - 8,8 - - - - 8,120 - - - - 8,64 - - - - 8,176 - - - - 496,32 - 32,26 - - - - - 496,88 - 32,26 - - - - - 496,144 - 32,26 - - - - 8,32 - 480,24 - - - 8,88 - 480,24 - - - 8,144 - 480,24 - - - Blueprint - 8,200 - 520,24 - - - 120,392 - 408,28 - - - - 8,392 - - - - - 8,232 - 168,152 - - - - 360,232 - 168,72 - - - - 184,312 - 168,72 - - - - 360,312 - 168,72 - - - - 184,232 - 168,72 - - - - 1 - 16,256 - - - - 1 - 16,280 - - - - 1 - 16,304 - - - - 1 - 16,328 - - - - 1 - 16,352 - - - - 368,256 - - - - 1 - 192,360 - - - - 192,256 - - - 1 - 1 - 16 - 280,256 - 64,21 - - - - 368,336 - - - - 1 - 448,336 - - - - 368,360 - - - - 448,360 - - - - 1 - 192,336 - - - - - - - Alt-F4 - Quit the application - - - - - - - - F1 - Show info about this application - - - - - -

B)++ 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