Selaa lähdekoodia

Merge pull request #988 from nockawa/master

First commit of the NormalHeightMap command line tool.
David Catuhe 9 vuotta sitten
vanhempi
commit
f4835f3f16

+ 22 - 0
Tools/NormalHeightMapTool/NormalHeightMapTool.sln

@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.24720.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NormalHeightMapTool", "NormalHeightMapTool\NormalHeightMapTool.csproj", "{2F997E8B-E471-4703-AD73-366BB48AB825}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{2F997E8B-E471-4703-AD73-366BB48AB825}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2F997E8B-E471-4703-AD73-366BB48AB825}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2F997E8B-E471-4703-AD73-366BB48AB825}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2F997E8B-E471-4703-AD73-366BB48AB825}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 6 - 0
Tools/NormalHeightMapTool/NormalHeightMapTool/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
+    </startup>
+</configuration>

+ 66 - 0
Tools/NormalHeightMapTool/NormalHeightMapTool/NormalHeightMapTool.csproj

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{2F997E8B-E471-4703-AD73-366BB48AB825}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>NormalHeightMapTool</RootNamespace>
+    <AssemblyName>NormalHeightMapTool</AssemblyName>
+    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="ImageProcessor, Version=2.3.2.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\ImageProcessor.2.3.2.0\lib\net45\ImageProcessor.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 276 - 0
Tools/NormalHeightMapTool/NormalHeightMapTool/Program.cs

@@ -0,0 +1,276 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using ImageProcessor;
+
+namespace NormalHeightMapTool
+{
+	class Program
+	{
+		private static List<Tuple<string, string>> _switches;
+
+		static void Main(string[] args)
+		{
+			// Display Help
+			if (args.Length == 0 || args.Any(a => string.Compare(a, "-h", StringComparison.InvariantCultureIgnoreCase) == 0))
+			{
+				DisplayUsage();
+				return;
+			}
+
+			// Construct a switch list, items1 of tuple is lowercast switch value without the dash, item2 is the value (if any)
+			if (BuildSwitches(args) == false)
+			{
+				DisplayUsage();
+				return;
+			}
+
+			// Check for pack operation
+			if (_switches[0].Item1 == "pack")
+			{
+				Pack();
+			}
+
+			// Check for invert operation
+			else if (_switches[0].Item1 == "invert")
+			{
+				Invert();
+			}
+
+			// Not either one of them, display usage and quit
+			else
+			{
+				DisplayUsage();
+				return;
+			}
+		}
+
+		private static bool BuildSwitches(string[] args)
+		{
+			_switches = new List<Tuple<string, string>>();
+			foreach (var a in args)
+			{
+				if (string.IsNullOrWhiteSpace(a) || a[0] != '-')
+				{
+					return false;
+				}
+
+				var sep = a.IndexOf(":", StringComparison.Ordinal);
+				if (sep == -1)
+				{
+					_switches.Add(new Tuple<string, string>(a.Substring(1).ToLowerInvariant(), null));
+				}
+				else
+				{
+					_switches.Add(new Tuple<string, string>(a.Substring(1, sep - 1).ToLowerInvariant(), a.Substring(sep + 1)));
+				}
+			}
+			return true;
+		}
+
+		private static void Invert()
+		{
+			var pfn = _switches[0].Item2;
+			if (File.Exists(pfn) == false)
+			{
+				Console.WriteLine("Can't find file: " + pfn + "\r\nSorry...");
+				return;
+			}
+
+			var saveSwitch = _switches.FirstOrDefault(s => s.Item1 == "save");
+			if (saveSwitch == null || string.IsNullOrWhiteSpace(saveSwitch.Item2))
+			{
+				Console.WriteLine("Must contain a -save:<targetfilepathname> to save the file.Sorry...");
+				return;
+			}
+
+			try
+			{
+				var invertR = _switches.Any(s => s.Item1=="r");
+				var invertG = _switches.Any(s => s.Item1=="g");
+
+				var fileData = File.ReadAllBytes(pfn);
+				using (var stream = new MemoryStream(fileData))
+				{
+					using (var factory = new ImageFactory())
+					{
+						factory.Load(stream);
+						var img = (Bitmap)factory.Image;
+						if (img == null)
+						{
+							Console.WriteLine("Unsupported file format or corrupted. Sorry...");
+							return;
+						}
+
+						Console.WriteLine("Working...");
+						for (int y = 0; y < img.Height; y++)
+						{
+							for (int x = 0; x < img.Width; x++)
+							{
+								var p = img.GetPixel(x, y);
+
+								var r = (invertR) ? (255 - p.R) : p.R;		// This is the invert operation on R
+								var g = (invertG) ? (255 - p.G) : p.G;		//  and G...
+								
+								var np = Color.FromArgb(p.A, r, g, p.B);
+
+								img.SetPixel(x, y, np);
+
+							}
+						}
+
+						Console.WriteLine("Saving to: " + saveSwitch.Item2);
+						var spfn = saveSwitch.Item2;
+						if (string.IsNullOrWhiteSpace(Path.GetDirectoryName(spfn)))
+						{
+							spfn = Path.Combine(Directory.GetCurrentDirectory(), spfn);
+						}
+						factory.Save(spfn);
+						Console.WriteLine("Job's done! Bye...");
+					}
+				}
+			}
+			catch (Exception ex)
+			{
+				Console.WriteLine("OOOOOOOPS\r\n\r\n" + ex.ToString());
+				return;
+			}
+		}
+
+		private static void Pack()
+		{
+			var nswitch = _switches.FirstOrDefault(s => s.Item1=="normalmap");
+			if (nswitch == null || string.IsNullOrWhiteSpace(nswitch.Item2) || File.Exists(nswitch.Item2) == false)
+			{
+				Console.WriteLine("You have to specify a valid file for -normalmap. Sorry...");
+				return;
+			}
+
+			var hswitch = _switches.FirstOrDefault(s => s.Item1=="heightmap");
+			if (hswitch == null || string.IsNullOrWhiteSpace(hswitch.Item2) || File.Exists(hswitch.Item2) == false)
+			{
+				Console.WriteLine("You have to specify a valid file for -heightmap. Sorry...");
+				return;
+			}
+
+			var saveSwitch = _switches.FirstOrDefault(s => s.Item1 == "save");
+			if (saveSwitch == null || string.IsNullOrWhiteSpace(saveSwitch.Item2))
+			{
+				Console.WriteLine("Must contain a -save:<targetfilepathname> to save the file.Sorry...");
+				return;
+			}
+
+			var cswitch = _switches.FirstOrDefault(s => s.Item1=="heightmapchannel");
+			var channel = "r";
+			if (cswitch != null)
+			{
+				channel = cswitch.Item2.ToLowerInvariant();
+				switch (channel)
+				{
+					case "a":
+					case "r":
+					case "g":
+					case "b":
+						break;
+					default:
+						Console.WriteLine("You didn't specify a valid channel, only A, R, G, B are valid. Sorry...");
+						return;
+				}
+			}
+
+			var normalFileData = File.ReadAllBytes(nswitch.Item2);
+			var heightFileData = File.ReadAllBytes(hswitch.Item2);
+
+			using (var normalStream = new MemoryStream(normalFileData))
+			using (var heightStream = new MemoryStream(heightFileData))
+			{
+				using (var normalFactory = new ImageFactory()) 
+				using (var heightFactory = new ImageFactory()) 
+				{
+					normalFactory.Load(normalStream);
+					heightFactory.Load(heightStream);
+
+					var normalImg = (Bitmap)normalFactory.Image;
+					var heightImg = (Bitmap)heightFactory.Image;
+
+					if (normalImg.PixelFormat != PixelFormat.Format32bppArgb && normalImg.PixelFormat != PixelFormat.Format32bppPArgb)
+					{
+						Console.WriteLine("The normalmap source file must be argb, sorry lazy me doesn't suport rgb only...");
+						return;
+					}
+
+					try
+					{
+						Console.WriteLine("Working...");
+						for (int y = 0; y < normalImg.Height; y++)
+						{
+							for (int x = 0; x < normalImg.Width; x++)
+							{
+								var normalPix = normalImg.GetPixel(x, y);
+								var heightPix = heightImg.GetPixel(x, y);
+
+								int alpha;
+								switch (channel)
+								{
+									case "a":
+										alpha = heightPix.A;
+										break;
+									case "r":
+										alpha = heightPix.R;
+										break;
+									case "g":
+										alpha = heightPix.G;
+										break;
+									case "b":
+										alpha = heightPix.B;
+										break;
+									default:
+										alpha = heightPix.R;
+										break;
+								}
+
+								var newPixel = Color.FromArgb(alpha<<24 | (normalPix.ToArgb()&0XFFFFFF));
+								normalImg.SetPixel(x, y, newPixel);
+							}
+						}
+						Console.WriteLine("Saving to: " + saveSwitch.Item2);
+
+						var spfn = saveSwitch.Item2;
+						if (string.IsNullOrWhiteSpace(Path.GetDirectoryName(spfn)))
+						{
+							spfn = Path.Combine(Directory.GetCurrentDirectory(), spfn);
+						}
+						normalFactory.Save(spfn);
+						Console.WriteLine("Job's done! Bye...");
+					}
+					catch (Exception ex)
+					{
+						Console.WriteLine("OOOOOOOPS\r\n\r\n" + ex.ToString());
+						return;
+					}
+				}
+			}
+		}
+
+		public static void DisplayUsage()
+		{
+				var helpMessage = 
+@"
+Normal/HeightMap Tool
+=====================
+
+Usage:
+ To pack a normal map file and a height map file in one single file (height map will be stored in alpha channel and the value will be taken in the Red component of the source heightmap if -heightmapchannel is not specified):
+ -pack -normalmap:<filepathname> -heightmap:<filepathname> [-heightmapchannel:R|G|B|A] -save:<targetfilepathname>
+
+ To invert the R &| G component of a normal map:
+ -invert:<normalmapfilepathname> [-R] [-G] -save:<targetfilepathname>
+
+";
+			Console.WriteLine(helpMessage);		
+		}
+	}
+}

+ 36 - 0
Tools/NormalHeightMapTool/NormalHeightMapTool/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NormalHeightMapTool")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("NormalHeightMapTool")]
+[assembly: AssemblyCopyright("Copyright ©  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("2f997e8b-e471-4703-ad73-366bb48ab825")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 4 - 0
Tools/NormalHeightMapTool/NormalHeightMapTool/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="ImageProcessor" version="2.3.2.0" targetFramework="net452" />
+</packages>

+ 25 - 0
Tools/NormalHeightMapTool/ReadMe.md

@@ -0,0 +1,25 @@
+Normal/Height Map Tool
+======================
+
+This little command line tool is used to perform these tasks:
+
+- Pack two distinct normal map and height map files into a single one, store the height in the alpha channel.
+
+- Perform Color based invert on R and/or G channel of a normal map to reoriente the normal vector.
+
+You can find a single .exe file in the Redist folder, if you build the project beware that the ImageProcessor.dll must be merged or bring along with the exe.
+
+
+### Command line usage:
+```
+ To pack a normal map file and a height map file in one single file (height map will be stored in alpha channel and the value will be taken in the Red component of the source heightmap if -heightmapchannel is not specified):
+ -pack -normalmap:<filepathname> -heightmap:<filepathname> [-heightmapchannel:R|G|B|A] -save:<targetfilepathname>
+
+ To invert the R &| G component of a normal map:
+ -invert:<normalmapfilepathname> [-R] [-G] -save:<targetfilepathname>
+```
+
+### Acknowledgement
+This tool use the [ImageProcessor library](http://imageprocessor.org/)
+
+Exe+dll are merged thanks to [ILMerge-GUI](http://ilmergegui.codeplex.com//)

BIN
Tools/NormalHeightMapTool/Redist/NormalHeightMapTool.exe