Эх сурвалжийг харах

Merge remote-tracking branch 'origin/master' into VSE285_Navigation

weinand 9 жил өмнө
parent
commit
55b6a9f9a4
97 өөрчлөгдсөн 6080 нэмэгдсэн , 317 устгасан
  1. 2 2
      Build/CMake/Modules/AtomicDesktop.cmake
  2. 6 1
      Build/CMake/Modules/AtomicWindows.cmake
  3. 23 0
      LICENSE.md
  4. 201 15
      Script/AtomicEditor/hostExtensions/HostExtensionServices.ts
  5. 10 5
      Script/AtomicEditor/hostExtensions/ServiceLocator.ts
  6. 135 0
      Script/AtomicEditor/hostExtensions/coreExtensions/ProjectBasedExtensionLoader.ts
  7. 5 5
      Script/AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts
  8. 9 0
      Script/AtomicEditor/ui/EditorUI.ts
  9. 0 4
      Script/AtomicEditor/ui/frames/MainFrame.ts
  10. 19 3
      Script/AtomicEditor/ui/frames/menus/HierarchyFrameMenu.ts
  11. 32 0
      Script/AtomicEditor/ui/frames/menus/MainFrameMenu.ts
  12. 17 6
      Script/AtomicEditor/ui/frames/menus/MenuItemSources.ts
  13. 15 2
      Script/AtomicEditor/ui/frames/menus/ProjectFrameMenu.ts
  14. 45 0
      Script/AtomicEditor/ui/modal/ExtensionWindow.ts
  15. 10 0
      Script/AtomicEditor/ui/modal/ModalOps.ts
  16. 1 1
      Script/AtomicWebViewEditor/clientExtensions/ClientExtensionEventNames.ts
  17. 11 3
      Script/AtomicWebViewEditor/clientExtensions/ClientExtensionServices.ts
  18. 25 5
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/TypescriptLanguageExtension.ts
  19. 23 9
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageService.ts
  20. 19 8
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageServiceWebWorker.ts
  21. 11 4
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerProcessTypes.ts
  22. 16 0
      Script/AtomicWebViewEditor/editor/editorCommands.ts
  23. 18 4
      Script/AtomicWebViewEditor/interop.ts
  24. 1 0
      Script/AtomicWebViewEditor/tsconfig.json
  25. 2 2
      Script/Packages/Atomic/Core.json
  26. 9 2
      Script/Packages/Atomic/Scene.json
  27. 1 1
      Script/TypeScript/AtomicWork.d.ts
  28. 53 12
      Script/TypeScript/EditorWork.d.ts
  29. 17 0
      Script/TypeScript/duktape.d.ts
  30. 3 0
      Script/tsconfig.json
  31. 66 138
      Source/Atomic/Engine/EngineConfig.cpp
  32. 14 18
      Source/Atomic/Engine/EngineConfig.h
  33. 8 0
      Source/Atomic/Graphics/Direct3D9/D3D9ShaderVariation.cpp
  34. 29 0
      Source/Atomic/IPC/IPC.cpp
  35. 8 0
      Source/Atomic/IPC/IPC.h
  36. 14 0
      Source/Atomic/IPC/IPCWindows.cpp
  37. 1 1
      Source/Atomic/IPC/IPCWindows.h
  38. 6 0
      Source/Atomic/IPC/IPCWorker.cpp
  39. 149 0
      Source/Atomic/Resource/Configuration.cpp
  40. 66 0
      Source/Atomic/Resource/Configuration.h
  41. 153 0
      Source/Atomic/Resource/Image.cpp
  42. 6 0
      Source/Atomic/Resource/Image.h
  43. 1 1
      Source/Atomic/Resource/JSONFile.cpp
  44. 20 1
      Source/Atomic/Resource/ResourceCache.cpp
  45. 23 0
      Source/Atomic/UI/UISelectItem.cpp
  46. 5 0
      Source/Atomic/UI/UISelectItem.h
  47. 3 4
      Source/AtomicEditor/Application/AEEditorCommon.cpp
  48. 17 8
      Source/AtomicEditor/Editors/SceneEditor3D/Gizmo3D.cpp
  49. 1 0
      Source/AtomicEditor/Editors/SceneEditor3D/Gizmo3D.h
  50. 3 0
      Source/AtomicWebView/Internal/WebAppBrowser.cpp
  51. 17 8
      Source/AtomicWebView/WebBrowserHost.cpp
  52. 24 0
      Source/AtomicWebView/WebBrowserHost.h
  53. 14 3
      Source/AtomicWebView/WebClient.cpp
  54. 15 1
      Source/AtomicWebView/WebViewJS.cpp
  55. 1 0
      Source/ThirdParty/CMakeLists.txt
  56. 36 23
      Source/ThirdParty/SDL/src/video/cocoa/SDL_cocoaevents.m
  57. 3 0
      Source/ThirdParty/libsquish/CMakeLists.txt
  58. 20 0
      Source/ThirdParty/libsquish/LICENSE
  59. 35 0
      Source/ThirdParty/libsquish/README
  60. 350 0
      Source/ThirdParty/libsquish/alpha.cpp
  61. 41 0
      Source/ThirdParty/libsquish/alpha.h
  62. 392 0
      Source/ThirdParty/libsquish/clusterfit.cpp
  63. 61 0
      Source/ThirdParty/libsquish/clusterfit.h
  64. 214 0
      Source/ThirdParty/libsquish/colourblock.cpp
  65. 41 0
      Source/ThirdParty/libsquish/colourblock.h
  66. 54 0
      Source/ThirdParty/libsquish/colourfit.cpp
  67. 56 0
      Source/ThirdParty/libsquish/colourfit.h
  68. 121 0
      Source/ThirdParty/libsquish/colourset.cpp
  69. 58 0
      Source/ThirdParty/libsquish/colourset.h
  70. 49 0
      Source/ThirdParty/libsquish/config.h
  71. 259 0
      Source/ThirdParty/libsquish/maths.cpp
  72. 233 0
      Source/ThirdParty/libsquish/maths.h
  73. 201 0
      Source/ThirdParty/libsquish/rangefit.cpp
  74. 54 0
      Source/ThirdParty/libsquish/rangefit.h
  75. 32 0
      Source/ThirdParty/libsquish/simd.h
  76. 183 0
      Source/ThirdParty/libsquish/simd_float.h
  77. 172 0
      Source/ThirdParty/libsquish/singlecolourfit.cpp
  78. 58 0
      Source/ThirdParty/libsquish/singlecolourfit.h
  79. 1064 0
      Source/ThirdParty/libsquish/singlecolourlookup.inl
  80. 260 0
      Source/ThirdParty/libsquish/squish.cpp
  81. 269 0
      Source/ThirdParty/libsquish/squish.h
  82. 21 0
      Source/ToolCore/Assets/AssetDatabase.cpp
  83. 1 0
      Source/ToolCore/Assets/AssetDatabase.h
  84. 16 9
      Source/ToolCore/Assets/PrefabImporter.cpp
  85. 3 0
      Source/ToolCore/Assets/PrefabImporter.h
  86. 36 5
      Source/ToolCore/Assets/TextureImporter.cpp
  87. 2 0
      Source/ToolCore/Assets/TextureImporter.h
  88. 6 2
      Source/ToolCore/Build/BuildBase.cpp
  89. 1 1
      Source/ToolCore/Build/BuildBase.h
  90. 23 0
      Source/ToolCore/Build/BuildMac.cpp
  91. 1 0
      Source/ToolCore/Build/BuildMac.h
  92. 22 0
      Source/ToolCore/Build/BuildWindows.cpp
  93. 1 0
      Source/ToolCore/Build/BuildWindows.h
  94. 108 0
      Source/ToolCore/Import/ImportConfig.cpp
  95. 58 0
      Source/ToolCore/Import/ImportConfig.h
  96. 58 0
      Source/ToolCore/Import/OpenAssetImporter.cpp
  97. 4 0
      Source/ToolCore/Import/OpenAssetImporter.h

+ 2 - 2
Build/CMake/Modules/AtomicDesktop.cmake

@@ -3,9 +3,9 @@ include(AtomicCommon)
 
 include_directories(${CMAKE_SOURCE_DIR}/Source/ThirdParty/Poco/Foundation/include)
 
-add_definitions( -DATOMIC_NAVIGATION -DATOMIC_TBUI -DATOMIC_FILEWATCHER -DPOCO_NO_AUTOMATIC_LIBS -DPOCO_STATIC )
+add_definitions( -DATOMIC_PLATFORM_DESKTOP -DATOMIC_NAVIGATION -DATOMIC_TBUI -DATOMIC_FILEWATCHER -DPOCO_NO_AUTOMATIC_LIBS -DPOCO_STATIC )
 
-set (ATOMIC_LINK_LIBRARIES ${ATOMIC_LINK_LIBRARIES} LibCpuId SQLite)
+set (ATOMIC_LINK_LIBRARIES ${ATOMIC_LINK_LIBRARIES} LibCpuId SQLite libsquish)
 
 # Check whether the CEF submodule is available
 if (EXISTS ${CMAKE_SOURCE_DIR}/Submodules/CEF)

+ 6 - 1
Build/CMake/Modules/AtomicWindows.cmake

@@ -29,10 +29,15 @@ else()
 
 endif()
 
+# removes dependency on D3DCompiler dll for Atomic Direct3D9 builds which don't require it
+# (binaries that never initialize the Direct3D9 graphics subsystem)
+if (NOT ATOMIC_D3DCOMPILER_DISABLE AND NOT ATOMIC_UWEBKIT)
+  add_definitions(-DATOMIC_D3DCOMPILER_ENABLED)
+endif()
 
 
 # compile with static runtime
-set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE)
+set( CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO )
 
 foreach(CompilerFlag ${CompilerFlags})
     string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")

+ 23 - 0
LICENSE.md

@@ -187,6 +187,29 @@ required.
 misrepresented as being the original software.
 3. This notice may not be removed or altered from any source distribution.
 
+#### libsquish license
+--------------
+
+Copyright (c) 2006 Simon Brown                          [email protected]
+
+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.
 
 #### Civetweb license
 ----------------

+ 201 - 15
Script/AtomicEditor/hostExtensions/HostExtensionServices.ts

@@ -22,12 +22,14 @@
 
 import * as EditorEvents from "../editor/EditorEvents";
 import * as EditorUI from "../ui/EditorUI";
-
-
+import MainFramMenu = require("../ui/frames/menus/MainFrameMenu");
+import HierarchyFrameMenu = require("../ui/frames/menus/HierarchyFrameMenu");
+import ProjectFrameMenu = require("../ui/frames/menus/ProjectFrameMenu");
+import ModalOps = require("../ui/modal/ModalOps");
 /**
  * Generic registry for storing Editor Extension Services
  */
-class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Editor.Extensions.ServiceRegistry<T> {
+export class ServicesProvider<T extends Editor.Extensions.ServiceEventListener> implements Editor.Extensions.ServicesProvider<T> {
     registeredServices: T[] = [];
 
     /**
@@ -38,9 +40,15 @@ class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Edit
         this.registeredServices.push(service);
     }
 
+    unregister(service: T) {
+        var index = this.registeredServices.indexOf(service, 0);
+        if (index > -1) {
+            this.registeredServices.splice(index, 1);
+        }
+    }
 }
 
-interface ServiceEventSubscriber {
+export interface ServiceEventSubscriber {
     /**
      * Allow this service registry to subscribe to events that it is interested in
      * @param  {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
@@ -51,7 +59,7 @@ interface ServiceEventSubscriber {
 /**
  * Registry for service extensions that are concerned about project events
  */
-export class ProjectServiceRegistry extends ServiceRegistry<Editor.HostExtensions.ProjectService> implements ServiceEventSubscriber {
+export class ProjectServicesProvider extends ServicesProvider<Editor.HostExtensions.ProjectServicesEventListener> implements Editor.HostExtensions.ProjectServicesProvider {
     constructor() {
         super();
     }
@@ -71,16 +79,18 @@ export class ProjectServiceRegistry extends ServiceRegistry<Editor.HostExtension
      * @param  {[type]} data Event info from the project unloaded event
      */
     projectUnloaded(data) {
-        this.registeredServices.forEach((service) => {
+        // Need to use a for loop for length down to 0 because extensions *could* delete themselves from the list on projectUnloaded
+        for (let i = this.registeredServices.length - 1; i >= 0; i--) {
+            let service = this.registeredServices[i];
             // Notify services that the project has been unloaded
             try {
                 if (service.projectUnloaded) {
                     service.projectUnloaded();
                 }
             } catch (e) {
-                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e} \n\n ${e.stack}`);
             }
-        });
+        };
     }
 
     /**
@@ -88,16 +98,18 @@ export class ProjectServiceRegistry extends ServiceRegistry<Editor.HostExtension
      * @param  {[type]} data Event info from the project unloaded event
      */
     projectLoaded(ev: Editor.EditorEvents.LoadProjectEvent) {
-        this.registeredServices.forEach((service) => {
+        // Need to use a for loop and don't cache the length because the list of services *may* change while processing.  Extensions could be appended to the end
+        for (let i = 0; i < this.registeredServices.length; i++) {
+            let service = this.registeredServices[i];
             try {
                 // Notify services that the project has just been loaded
                 if (service.projectLoaded) {
                     service.projectLoaded(ev);
                 }
             } catch (e) {
-                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
             }
-        });
+        };
     }
 
     playerStarted() {
@@ -117,7 +129,7 @@ export class ProjectServiceRegistry extends ServiceRegistry<Editor.HostExtension
 /**
  * Registry for service extensions that are concerned about Resources
  */
-export class ResourceServiceRegistry extends ServiceRegistry<Editor.HostExtensions.ResourceService> {
+export class ResourceServicesProvider extends ServicesProvider<Editor.HostExtensions.ResourceServicesEventListener> implements Editor.HostExtensions.ResourceServicesProvider {
     constructor() {
         super();
     }
@@ -145,7 +157,7 @@ export class ResourceServiceRegistry extends ServiceRegistry<Editor.HostExtensio
                     service.save(ev);
                 }
             } catch (e) {
-                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
             }
         });
     }
@@ -161,7 +173,7 @@ export class ResourceServiceRegistry extends ServiceRegistry<Editor.HostExtensio
                     service.delete(ev);
                 }
             } catch (e) {
-                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n ${e}\n ${e.stack}`);
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
             }
         });
     }
@@ -178,9 +190,183 @@ export class ResourceServiceRegistry extends ServiceRegistry<Editor.HostExtensio
                     service.rename(ev);
                 }
             } catch (e) {
-                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
             }
         });
     }
 
 }
+
+/**
+ * Registry for service extensions that are concerned about and need access to parts of the editor user interface
+ * Note: we may want to move this out into it's own file since it has a bunch of editor dependencies
+ */
+export class UIServicesProvider extends ServicesProvider<Editor.HostExtensions.UIServicesEventListener> implements Editor.HostExtensions.UIServicesProvider {
+    constructor() {
+        super();
+    }
+
+    private mainFrameMenu: MainFramMenu = null;
+    private hierarchyFrameMenu: HierarchyFrameMenu = null;
+    private projectFrameMenu: ProjectFrameMenu = null;
+    private modalOps: ModalOps;
+
+    init(mainFrameMenu: MainFramMenu, hierarchyFrameMenu: HierarchyFrameMenu, projectFrameMenu: ProjectFrameMenu, modalOps: ModalOps) {
+        // Only set these once
+        if (this.mainFrameMenu == null) {
+            this.mainFrameMenu = mainFrameMenu;
+        }
+        if (this.hierarchyFrameMenu == null) {
+            this.hierarchyFrameMenu = hierarchyFrameMenu;
+        }
+        if (this.projectFrameMenu == null) {
+            this.projectFrameMenu = projectFrameMenu;
+        }
+        if (this.modalOps == null) {
+            this.modalOps = modalOps;
+        }
+    }
+
+    /**
+     * Adds a new menu to the plugin menu
+     * @param  {string} id
+     * @param  {any} items
+     * @return {Atomic.UIMenuItemSource}
+     */
+    createPluginMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
+        return this.mainFrameMenu.createPluginMenuItemSource(id, items);
+    }
+
+    /**
+     * Removes a previously added menu from the plugin menu
+     * @param  {string} id
+     */
+    removePluginMenuItemSource(id: string) {
+        this.mainFrameMenu.removePluginMenuItemSource(id);
+    }
+
+    /**
+     * Adds a new menu to the hierarchy context menu
+     * @param  {string} id
+     * @param  {any} items
+     * @return {Atomic.UIMenuItemSource}
+     */
+    createHierarchyContextMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
+        return this.hierarchyFrameMenu.createPluginItemSource(id, items);
+    }
+
+    /**
+     * Removes a previously added menu from the hierarchy context menu
+     * @param  {string} id
+     */
+    removeHierarchyContextMenuItemSource(id: string) {
+        this.hierarchyFrameMenu.removePluginItemSource(id);
+    }
+
+    /**
+     * Adds a new menu to the project context menu
+     * @param  {string} id
+     * @param  {any} items
+     * @return {Atomic.UIMenuItemSource}
+     */
+    createProjectContextMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
+        return this.projectFrameMenu.createPluginItemSource(id, items);
+    }
+
+    /**
+     * Removes a previously added menu from the project context menu
+     * @param  {string} id
+     */
+    removeProjectContextMenuItemSource(id: string) {
+        this.projectFrameMenu.removePluginItemSource(id);
+    }
+
+    /**
+     * Disaplays a modal window
+     * @param  {Editor.Modal.ModalWindow} window
+     */
+    showModalWindow(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void): Editor.Modal.ExtensionWindow {
+        return this.modalOps.showExtensionWindow(windowText, uifilename, handleWidgetEventCB);
+    }
+
+    /**
+     * Called when a menu item has been clicked
+     * @param  {string} refId
+     * @type {boolean} return true if handled
+     */
+    menuItemClicked(refid: string): boolean {
+        // run through and find any services that can handle this.
+        return this.registeredServices.some((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can handle it
+                if (service.menuItemClicked) {
+                    if (service.menuItemClicked(refid)) {
+                        return true;
+                    }
+                }
+            } catch (e) {
+               EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when a context menu item in the hierarchy pane has been clicked
+     * @param  {Atomic.Node} node
+     * @param  {string} refId
+     * @type {boolean} return true if handled
+     */
+    hierarchyContextItemClicked(node: Atomic.Node, refid: string): boolean {
+        if (!node) 
+            return false;
+
+        // run through and find any services that can handle this.
+        return this.registeredServices.some((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can handle it
+                if (service.hierarchyContextItemClicked) {
+                    if (service.hierarchyContextItemClicked(node, refid)) {
+                        return true;
+                    }
+                }
+            } catch (e) {
+               EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+            }
+        });
+    }
+
+
+    /**
+     * Called when a context menu item in the hierarchy pane has been clicked
+     * @param  {ToolCore.Asset} asset
+     * @param  {string} refId
+     * @type {boolean} return true if handled
+     */
+    projectContextItemClicked(asset: ToolCore.Asset, refid: string): boolean {
+        if (!asset)
+            return false;
+
+        // run through and find any services that can handle this.
+        return this.registeredServices.some((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can handle it
+                if (service.projectContextItemClicked) {
+                    if (service.projectContextItemClicked(asset, refid)) {
+                        return true;
+                    }
+                }
+            } catch (e) {
+               EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
+     */
+    subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
+        // Placeholder for when UI events published by the editor need to be listened for
+        //eventDispatcher.subscribeToEvent(EditorEvents.SaveResourceNotification, (ev) => this.doSomeUiMessage(ev));
+    }
+}

+ 10 - 5
Script/AtomicEditor/hostExtensions/ServiceLocator.ts

@@ -22,6 +22,7 @@
 
 import * as HostExtensionServices from "./HostExtensionServices";
 import * as EditorUI from "../ui/EditorUI";
+import ProjectBasedExtensionLoader from "./coreExtensions/ProjectBasedExtensionLoader";
 import TypescriptLanguageExtension from "./languageExtensions/TypscriptLanguageExtension";
 
 /**
@@ -31,20 +32,22 @@ import TypescriptLanguageExtension from "./languageExtensions/TypscriptLanguageE
 export class ServiceLocatorType implements Editor.HostExtensions.HostServiceLocator {
 
     constructor() {
-        this.resourceServices = new HostExtensionServices.ResourceServiceRegistry();
-        this.projectServices = new HostExtensionServices.ProjectServiceRegistry();
+        this.resourceServices = new HostExtensionServices.ResourceServicesProvider();
+        this.projectServices = new HostExtensionServices.ProjectServicesProvider();
+        this.uiServices = new HostExtensionServices.UIServicesProvider();
     }
 
     private eventDispatcher: Atomic.UIWidget = null;
 
-    resourceServices: HostExtensionServices.ResourceServiceRegistry;
-    projectServices: HostExtensionServices.ProjectServiceRegistry;
+    resourceServices: HostExtensionServices.ResourceServicesProvider;
+    projectServices: HostExtensionServices.ProjectServicesProvider;
+    uiServices: HostExtensionServices.UIServicesProvider;
 
     loadService(service: Editor.HostExtensions.HostEditorService) {
         try {
             service.initialize(this);
         } catch (e) {
-            EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+            EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
         }
     }
 
@@ -56,6 +59,7 @@ export class ServiceLocatorType implements Editor.HostExtensions.HostServiceLoca
         this.eventDispatcher = frame;
         this.resourceServices.subscribeToEvents(this);
         this.projectServices.subscribeToEvents(this);
+        this.uiServices.subscribeToEvents(this);
     }
 
     /**
@@ -85,4 +89,5 @@ const serviceLocator = new ServiceLocatorType();
 export default serviceLocator;
 
 // Load up all the internal services
+serviceLocator.loadService(new ProjectBasedExtensionLoader());
 serviceLocator.loadService(new TypescriptLanguageExtension());

+ 135 - 0
Script/AtomicEditor/hostExtensions/coreExtensions/ProjectBasedExtensionLoader.ts

@@ -0,0 +1,135 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// 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.
+//
+
+/// <reference path="../../../TypeScript/duktape.d.ts" />
+
+import * as EditorEvents from "../../editor/EditorEvents";
+
+/**
+ * Resource extension that supports the web view typescript extension
+ */
+export default class ProjectBasedExtensionLoader implements Editor.HostExtensions.ProjectServicesEventListener {
+    name: string = "ProjectBasedExtensionLoader";
+    description: string = "This service supports loading extensions that reside in the project under {ProjectRoot}/Editor and named '*.Service.js'.";
+
+    private serviceRegistry: Editor.HostExtensions.HostServiceLocator = null;
+    private modSearchRewritten = false;
+
+    /**
+     * Prefix to use to detect "special" require paths
+     * @type {String}
+     */
+    private static duktapeRequirePrefix = "project:";
+
+    /**
+     * Inject this language service into the registry
+     * @return {[type]}             True if successful
+     */
+    initialize(serviceLocator: Editor.HostExtensions.HostServiceLocator) {
+
+        // Let's rewrite the mod search
+        this.rewriteModSearch();
+
+        // We care project events
+        serviceLocator.projectServices.register(this);
+        this.serviceRegistry = serviceLocator;
+    }
+
+    /**
+     * Rewrite the duktape modSearch routine so that we can intercept any
+     * require calls with a "project:" prefix.  Duktape will fail if it receives
+     * a require call with a fully qualified path starting with a "/" (at least on OSX and Linux),
+     * so we will need to detect any of these project level requires and allow Atomic to go to the
+     * file system and manually pull these in to provide to duktape
+     */
+    private rewriteModSearch() {
+        Duktape.modSearch = (function(origModSearch) {
+            return function(id: string, require, exports, module) {
+                let system = ToolCore.getToolSystem();
+                if (id.indexOf(ProjectBasedExtensionLoader.duktapeRequirePrefix) == 0) {
+                    let path = id.substr(ProjectBasedExtensionLoader.duktapeRequirePrefix.length) + ".js";
+
+                    // For safety, only allow bringing modules in from the project directory.  This could be
+                    // extended to look for some global extension directory to pull extensions from such as
+                    // ~/.atomicExtensions/...
+                    if (system.project && path.indexOf(system.project.projectPath) == 0) {
+                        console.log(`Searching for project based include: ${path}`);
+                        // we have a project based require
+                        if (Atomic.fileSystem.fileExists(path)) {
+                            let include = new Atomic.File(path, Atomic.FILE_READ);
+                            try {
+                                return include.readText();
+                            } finally {
+                                include.close();
+                            }
+                        } else {
+                            throw new Error(`Cannot find project module: ${path}`);
+                        }
+                    } else {
+                        throw new Error(`Extension at ${path} does not reside in the project directory ${system.project.projectPath}`);
+                    }
+                } else {
+                    return origModSearch(id, require, exports, module);
+                }
+            };
+        })(Duktape.modSearch);
+    }
+    /**
+     * Called when the project is being loaded to allow the typscript language service to reset and
+     * possibly compile
+     */
+    projectLoaded(ev: Editor.EditorEvents.LoadProjectEvent) {
+        // got a load, we need to reset the language service
+        console.log(`${this.name}: received a project loaded event for project at ${ev.path}`);
+        let system = ToolCore.getToolSystem();
+        if (system.project) {
+            let fileSystem = Atomic.getFileSystem();
+            let editorScriptsPath = Atomic.addTrailingSlash(system.project.resourcePath) + "EditorData/";
+            if (fileSystem.dirExists(editorScriptsPath)) {
+                let filenames = fileSystem.scanDir(editorScriptsPath, "*.js", Atomic.SCAN_FILES, true);
+                filenames.forEach((filename) => {
+                    // Filtered search in Atomic doesn't due true wildcarding, only handles extension filters
+                    // in the future this may be better handled with some kind of manifest file
+                    if (filename.toLowerCase().lastIndexOf(".plugin.js") >= 0) {
+                        var extensionPath = editorScriptsPath + filename;
+                        extensionPath = extensionPath.substring(0, extensionPath.length - 3);
+
+                        console.log(`Detected project extension at: ${extensionPath} `);
+                        // Note: duktape does not yet support unloading modules,
+                        // but will return the same object when passed a path the second time.
+                        let resourceServiceModule = require(ProjectBasedExtensionLoader.duktapeRequirePrefix + extensionPath);
+
+                        // Handle situation where the service is either exposed by a typescript default export
+                        // or as the module.export (depends on if it is being written in typescript, javascript, es6, etc.)
+                        let resourceService: Editor.HostExtensions.HostEditorService = null;
+                        if (resourceServiceModule.default) {
+                            resourceService = resourceServiceModule.default;
+                        } else {
+                            resourceService = resourceServiceModule;
+                        }
+                        this.serviceRegistry.loadService(resourceService);
+                    }
+                });
+            }
+        }
+    }
+}

+ 5 - 5
Script/AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts

@@ -25,7 +25,7 @@ import * as EditorEvents from "../../editor/EditorEvents";
 /**
  * Resource extension that supports the web view typescript extension
  */
-export default class TypescriptLanguageExtension implements Editor.HostExtensions.ResourceService, Editor.HostExtensions.ProjectService {
+export default class TypescriptLanguageExtension implements Editor.HostExtensions.ResourceServicesEventListener, Editor.HostExtensions.ProjectServicesEventListener {
     name: string = "HostTypeScriptLanguageExtension";
     description: string = "This service supports the typscript webview extension.";
 
@@ -107,11 +107,11 @@ export default class TypescriptLanguageExtension implements Editor.HostExtension
      * Inject this language service into the registry
      * @return {[type]}             True if successful
      */
-    initialize(serviceRegistry: Editor.HostExtensions.HostServiceLocator) {
+    initialize(serviceLocator: Editor.HostExtensions.HostServiceLocator) {
         // We care about both resource events as well as project events
-        serviceRegistry.resourceServices.register(this);
-        serviceRegistry.projectServices.register(this);
-        this.serviceRegistry = serviceRegistry;
+        serviceLocator.resourceServices.register(this);
+        serviceLocator.projectServices.register(this);
+        this.serviceRegistry = serviceLocator;
     }
 
     /**

+ 9 - 0
Script/AtomicEditor/ui/EditorUI.ts

@@ -24,6 +24,7 @@ import EditorEvents = require("editor/EditorEvents");
 import MainFrame = require("./frames/MainFrame");
 import ModalOps = require("./modal/ModalOps");
 import Shortcuts = require("./Shortcuts");
+import ServiceLocator from "../hostExtensions/ServiceLocator";
 
 // this is designed with public get functions to solve
 // circular dependency issues in TS
@@ -94,6 +95,14 @@ class EditorUI extends Atomic.ScriptObject {
     this.modalOps = new ModalOps();
     this.shortcuts = new Shortcuts();
 
+    // Hook the service locator into the event system and give it the ui objects it needs
+    ServiceLocator.uiServices.init(
+      this.mainframe.menu, 
+      this.mainframe.hierarchyFrame.menu,
+      this.mainframe.projectframe.menu,
+      this.modalOps);
+    ServiceLocator.subscribeToEvents(this.mainframe);
+
     this.subscribeToEvent(EditorEvents.ModalError, (event:EditorEvents.ModalErrorEvent) => {
       this.showModalError(event.title, event.message);
     });

+ 0 - 4
Script/AtomicEditor/ui/frames/MainFrame.ts

@@ -33,7 +33,6 @@ import ScriptWidget = require("ui/ScriptWidget");
 import MainFrameMenu = require("./menus/MainFrameMenu");
 
 import MenuItemSources = require("./menus/MenuItemSources");
-import ServiceLocator from "../../hostExtensions/ServiceLocator";
 import * as EditorEvents from "../../editor/EditorEvents";
 
 class MainFrame extends ScriptWidget {
@@ -75,9 +74,6 @@ class MainFrame extends ScriptWidget {
             this.disableProjectMenus();
         });
 
-        // Allow the service locator to hook into the event system
-        ServiceLocator.subscribeToEvents(this);
-
         this.showWelcomeFrame(true);
 
     }

+ 19 - 3
Script/AtomicEditor/ui/frames/menus/HierarchyFrameMenu.ts

@@ -24,15 +24,20 @@ import strings = require("ui/EditorStrings");
 import EditorEvents = require("editor/EditorEvents");
 import EditorUI = require("ui/EditorUI");
 import MenuItemSources = require("./MenuItemSources");
+import ServiceLocator from "../../../hostExtensions/ServiceLocator";
 
 class HierarchyFrameMenus extends Atomic.ScriptObject {
 
+    contentFolder: string;
+
+    private contextMenuItemSource: Atomic.UIMenuItemSource = null;
+
     constructor() {
 
         super();
 
         MenuItemSources.createMenuItemSource("hierarchy create items", createItems);
-        MenuItemSources.createMenuItemSource("node context general", nodeGeneralContextItems);
+        this.contextMenuItemSource = MenuItemSources.createMenuItemSource("node context general", nodeGeneralContextItems);
 
         this.subscribeToEvent(EditorEvents.ContentFolderChanged, (ev: EditorEvents.ContentFolderChangedEvent) => {
             this.contentFolder = ev.path;
@@ -101,6 +106,8 @@ class HierarchyFrameMenus extends Atomic.ScriptObject {
                 node.remove();
                 scene.sendEvent("SceneEditAddRemoveNodes", { end: true });
 
+                return true;
+
             } else if (refid == "duplicate_node") {
 
                 if (node instanceof Atomic.Scene)
@@ -108,9 +115,12 @@ class HierarchyFrameMenus extends Atomic.ScriptObject {
 
                 var newnode = node.clone();
                 node.scene.sendEvent("SceneEditNodeCreated", { node: newnode });
+
+                return true;
             }
 
-            return true;
+            // Let plugins handle context
+            return ServiceLocator.uiServices.hierarchyContextItemClicked(node, refid);
         }
 
         return false;
@@ -133,7 +143,13 @@ class HierarchyFrameMenus extends Atomic.ScriptObject {
 
     }
 
-    contentFolder: string;
+    createPluginItemSource(id: string, items: any): Atomic.UIMenuItemSource {
+        return MenuItemSources.createSubMenuItemSource(this.contextMenuItemSource , id, items);
+    }
+
+    removePluginItemSource(id: string) {
+        this.contextMenuItemSource.removeItemWithStr(id);
+    }
 
 }
 

+ 32 - 0
Script/AtomicEditor/ui/frames/menus/MainFrameMenu.ts

@@ -25,9 +25,12 @@ import EditorEvents = require("../../../editor/EditorEvents");
 import EditorUI = require("../../EditorUI");
 import MenuItemSources = require("./MenuItemSources");
 import Preferences = require("editor/Preferences");
+import ServiceLocator from "../../../hostExtensions/ServiceLocator";
 
 class MainFrameMenu extends Atomic.ScriptObject {
 
+    private pluginMenuItemSource: Atomic.UIMenuItemSource;
+
     constructor() {
 
         super();
@@ -41,6 +44,27 @@ class MainFrameMenu extends Atomic.ScriptObject {
 
     }
 
+    createPluginMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
+        if (!this.pluginMenuItemSource) {
+            var developerMenuItemSource = MenuItemSources.getMenuItemSource("menu developer");
+            this.pluginMenuItemSource = MenuItemSources.createSubMenuItemSource(developerMenuItemSource ,"Plugins", {});
+        }
+
+        return MenuItemSources.createSubMenuItemSource(this.pluginMenuItemSource , id, items);
+
+    }
+
+    removePluginMenuItemSource(id: string) {
+        if (this.pluginMenuItemSource) {
+            this.pluginMenuItemSource.removeItemWithStr(id);
+            if (0 == this.pluginMenuItemSource.itemCount) {
+                var developerMenuItemSource = MenuItemSources.getMenuItemSource("menu developer");
+                developerMenuItemSource.removeItemWithStr("Plugins");
+                this.pluginMenuItemSource = null;
+            }
+        }
+    }
+
     handlePopupMenu(target: Atomic.UIWidget, refid: string): boolean {
 
         if (target.id == "menu edit popup") {
@@ -218,12 +242,14 @@ class MainFrameMenu extends Atomic.ScriptObject {
             if (refid == "developer assetdatabase scan") {
 
               ToolCore.assetDatabase.scan();
+              return true;
 
             }
 
             if (refid == "developer assetdatabase force") {
 
               ToolCore.assetDatabase.reimportAllAssets();
+              return true;
 
             }
 
@@ -234,8 +260,12 @@ class MainFrameMenu extends Atomic.ScriptObject {
                 myPrefs.saveEditorWindowData(myPrefs.editorWindow);
                 myPrefs.savePlayerWindowData(myPrefs.playerWindow);
                 Atomic.getEngine().exit();
+                return true;
             }
 
+            // If we got here, then we may have been injected by a plugin.  Notify the plugins
+            return ServiceLocator.uiServices.menuItemClicked(refid);
+
         } else if (target.id == "menu tools popup") {
 
             if (refid == "tools toggle profiler") {
@@ -286,6 +316,8 @@ class MainFrameMenu extends Atomic.ScriptObject {
                 return true;
             }
 
+        } else {
+            console.log("Menu: " + target.id + " clicked");
         }
 
     }

+ 17 - 6
Script/AtomicEditor/ui/frames/menus/MenuItemSources.ts

@@ -77,12 +77,7 @@ function createMenuItemSourceRecursive(items: any): Atomic.UIMenuItemSource {
 
             }
             else if (typeof value === "object") {
-
-                var subsrc = createMenuItemSourceRecursive(value);
-
-                var item = new Atomic.UIMenuItem(key);
-                item.subSource = subsrc;
-                src.addItem(item);
+                createSubMenuItemSource(src, key, value);
 
             }
 
@@ -95,6 +90,16 @@ function createMenuItemSourceRecursive(items: any): Atomic.UIMenuItemSource {
 
 }
 
+export function createSubMenuItemSource(src: Atomic.UIMenuItemSource, id: string, items: any): Atomic.UIMenuItemSource {
+    var subsrc = createMenuItemSourceRecursive(items);
+
+    var item = new Atomic.UIMenuItem(id);
+    item.subSource = subsrc;
+    src.addItem(item);
+
+    return subsrc;
+}
+
 export function createMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
 
     srcLookup[id] = createMenuItemSourceRecursive(items);
@@ -102,3 +107,9 @@ export function createMenuItemSource(id: string, items: any): Atomic.UIMenuItemS
     return srcLookup[id];
 
 }
+
+export function deleteMenuItemSource(id: string) {
+    if (srcLookup[id]) {
+        delete srcLookup[id];
+    }
+}

+ 15 - 2
Script/AtomicEditor/ui/frames/menus/ProjectFrameMenu.ts

@@ -24,15 +24,20 @@ import strings = require("ui/EditorStrings");
 import EditorEvents = require("editor/EditorEvents");
 import EditorUI = require("ui/EditorUI");
 import MenuItemSources = require("./MenuItemSources");
+import ServiceLocator from "../../../hostExtensions/ServiceLocator";
 
 class ProjectFrameMenus extends Atomic.ScriptObject {
 
+    contentFolder: string;
+
+    private contextMenuItemSource: Atomic.UIMenuItemSource = null;
+
     constructor() {
 
         super();
 
         MenuItemSources.createMenuItemSource("asset context folder", assetFolderContextItems);
-        MenuItemSources.createMenuItemSource("asset context general", assetGeneralContextItems);
+        this.contextMenuItemSource = MenuItemSources.createMenuItemSource("asset context general", assetGeneralContextItems);
         MenuItemSources.createMenuItemSource("project create items", createItems);
 
         this.subscribeToEvent(EditorEvents.ContentFolderChanged, (ev: EditorEvents.ContentFolderChangedEvent) => {
@@ -106,6 +111,8 @@ class ProjectFrameMenus extends Atomic.ScriptObject {
                 return true;
             }
 
+            // Let plugins handle context
+            return ServiceLocator.uiServices.projectContextItemClicked(asset, refid);
         }
 
         return false;
@@ -149,7 +156,13 @@ class ProjectFrameMenus extends Atomic.ScriptObject {
 
     }
 
-    contentFolder: string;
+    createPluginItemSource(id: string, items: any): Atomic.UIMenuItemSource {
+        return MenuItemSources.createSubMenuItemSource(this.contextMenuItemSource , id, items);
+    }
+
+    removePluginItemSource(id: string) {
+        this.contextMenuItemSource.removeItemWithStr(id);
+    }
 
 }
 

+ 45 - 0
Script/AtomicEditor/ui/modal/ExtensionWindow.ts

@@ -0,0 +1,45 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// 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.
+//
+
+import EditorUI = require("../EditorUI");
+import ModalWindow = require("./ModalWindow");
+
+class ExtensionWindow extends ModalWindow {
+
+    private handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void;
+
+    constructor(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void) {
+
+        super();
+
+        this.init(windowText, uifilename);
+
+        this.handleWidgetEventCB = handleWidgetEventCB;
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
+        if (this.handleWidgetEventCB)
+            this.handleWidgetEventCB(ev);
+    }
+}
+
+export = ExtensionWindow;

+ 10 - 0
Script/AtomicEditor/ui/modal/ModalOps.ts

@@ -43,6 +43,8 @@ import UIResourceOps = require("./UIResourceOps");
 
 import SnapSettingsWindow = require("./SnapSettingsWindow");
 
+import ExtensionWindow = require("./ExtensionWindow");
+
 import ProjectTemplates = require("../../resources/ProjectTemplates");
 
 
@@ -280,6 +282,14 @@ class ModalOps extends Atomic.ScriptObject {
 
     }
 
+    showExtensionWindow(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void): Editor.Modal.ExtensionWindow {
+        if (this.show()) {
+
+            this.opWindow = new ExtensionWindow(windowText, uifilename, handleWidgetEventCB);
+            return this.opWindow;
+        }
+    }
+
     private show(): boolean {
 
         if (this.dimmer.parent) {

+ 1 - 1
Script/AtomicWebViewEditor/clientExtensions/ClientExtensionEventNames.ts

@@ -26,7 +26,7 @@
 export default class ClientExtensionEventNames {
     static CodeLoadedEvent = "CodeLoadedEvent";
     static ConfigureEditorEvent = "ConfigureEditorEvent";
-    static ResourceSavedEvent = "ResourceSavedEvent";
+    static CodeSavedEvent = "CodeSavedEvent";
     static ResourceRenamedEvent = "ResourceRenamedEvent";
     static ResourceDeletedEvent = "ResourceDeletedEvent";
     static ProjectUnloadedEvent = "ProjectUnloadedEvent";

+ 11 - 3
Script/AtomicWebViewEditor/clientExtensions/ClientExtensionServices.ts

@@ -54,7 +54,7 @@ export class EventDispatcher implements Editor.Extensions.EventDispatcher {
 /**
  * Generic registry for storing Editor Extension Services
  */
-class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Editor.Extensions.ServiceRegistry<T> {
+class ServiceRegistry<T extends Editor.Extensions.ServiceEventListener> implements Editor.Extensions.ServicesProvider<T> {
     registeredServices: T[] = [];
 
     /**
@@ -64,6 +64,13 @@ class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Edit
     register(service: T) {
         this.registeredServices.push(service);
     }
+
+    unregister(service: T) {
+        var index = this.registeredServices.indexOf(service, 0);
+        if (index > -1) {
+            this.registeredServices.splice(index, 1);
+        }
+    }
 }
 
 export class ExtensionServiceRegistry extends ServiceRegistry<Editor.ClientExtensions.WebViewService> {
@@ -78,6 +85,7 @@ export class ExtensionServiceRegistry extends ServiceRegistry<Editor.ClientExten
         eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ResourceRenamedEvent, (ev) => this.renameResource(ev));
         eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ProjectUnloadedEvent, (ev) => this.projectUnloaded());
         eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ResourceDeletedEvent, (ev) => this.deleteResource(ev));
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.CodeSavedEvent, (ev) => this.saveCode(ev));
     }
 
     /**
@@ -98,10 +106,10 @@ export class ExtensionServiceRegistry extends ServiceRegistry<Editor.ClientExten
     }
 
     /**
-     * Called after a resource has been saved
+     * Called after code has been saved
      * @param  {Editor.EditorEvents.SaveResourceEvent} ev
      */
-    saveResource(ev: Editor.EditorEvents.SaveResourceEvent) {
+    saveCode(ev: Editor.EditorEvents.CodeSavedEvent) {
         // run through and find any services that can handle this.
         this.registeredServices.forEach((service) => {
             try {

+ 25 - 5
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/TypescriptLanguageExtension.ts

@@ -144,9 +144,26 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
             case WorkerProcessTypes.Alert:
                 alert(e.data.message);
                 break;
+            case WorkerProcessTypes.AnnotationsUpdated:
+                this.setAnnotations(e.data);
+                break;
         }
     }
 
+    /**
+     * Set annotations based upon issues reported by the typescript language service
+     * @param  {WorkerProcessTypes.GetAnnotationsResponseMessageData} event
+     */
+    setAnnotations(event: WorkerProcessTypes.GetAnnotationsResponseMessageData) {
+        // grab the existing annotations and filter out any TS annotations
+        let oldAnnotations = this.editor.session.getAnnotations().filter(ann => !ann.tsAnnotation);
+        this.editor.session.clearAnnotations();
+
+        // Mark these annotations as special
+        event.annotations.forEach(ann => ann.tsAnnotation = true);
+        this.editor.session.setAnnotations(oldAnnotations.concat(event.annotations));
+    }
+
     /**
      * Build/Attach to the shared web worker
      */
@@ -215,13 +232,16 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
      * Called once a resource has been saved
      * @param  {Editor.EditorEvents.SaveResourceEvent} ev
      */
-    save(ev: Editor.EditorEvents.SaveResourceEvent) {
-        if (this.isValidFiletype(ev.path)) {
-            console.log(`${this.name}: received a save resource event for ${ev.path}`);
+    save(ev: Editor.EditorEvents.CodeSavedEvent) {
+        if (this.isValidFiletype(ev.filename)) {
+            console.log(`${this.name}: received a save resource event for ${ev.filename}`);
 
             const message: WorkerProcessTypes.SaveMessageData = {
-                command: ClientExtensionEventNames.ResourceSavedEvent,
-                path: ev.path
+                command: ClientExtensionEventNames.CodeSavedEvent,
+                filename: ev.filename,
+                fileExt: ev.fileExt,
+                code: ev.code,
+                editor: null // cannot send editor across the boundary
             };
 
             this.worker.port.postMessage(message);

+ 23 - 9
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageService.ts

@@ -163,6 +163,26 @@ export class TypescriptLanguageService {
     getProjectFiles(): string[] {
         return this.projectFiles;
     }
+
+    getPreEmitWarnings(filename: string, options?: ts.CompilerOptions) {
+        options = options || this.compilerOptions;
+
+        let allDiagnostics = this.compileFile(filename);
+        let results = [];
+
+        allDiagnostics.forEach(diagnostic => {
+            let lineChar = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
+            let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
+            results.push({
+                row: lineChar.line,
+                column: lineChar.character,
+                text: message,
+                type: diagnostic.category == 1 ? "error" : "warning"
+            });
+        });
+        return results;
+    }
+
     /**
      * Simply transpile the typescript file.  This is much faster and only checks for syntax errors
      * @param {string[]}           fileNames array of files to transpile
@@ -222,7 +242,7 @@ export class TypescriptLanguageService {
      * @param  {string}  a list of file names to compile
      * @param  {ts.CompilerOptions} options for the compiler
      */
-    compile(files: string[], options?: ts.CompilerOptions): void {
+    compile(files: string[], options?: ts.CompilerOptions): ts.Diagnostic[] {
         let start = new Date().getTime();
 
         options = options || this.compilerOptions;
@@ -242,18 +262,12 @@ export class TypescriptLanguageService {
         } else {
             // Only compile the files that are newly edited
             files.forEach(filename => {
-                // increment the version number since we changed
-                this.versionMap[filename].version++;
-                this.versionMap[filename].snapshot = null;
                 errors = errors.concat(this.compileFile(filename));
             });
         }
 
-        if (errors.length) {
-            this.logErrors(errors);
-        }
-
         console.log(`${this.name}: Compiling complete after ${new Date().getTime() - start} ms`);
+        return errors;
     }
 
     /**
@@ -285,7 +299,7 @@ export class TypescriptLanguageService {
         }
         let idx = this.projectFiles.indexOf(filepath);
         if (idx > -1) {
-            console.log(`Update project files array from ${filepath} to ${newpath}`)
+            console.log(`Update project files array from ${filepath} to ${newpath}`);
             this.projectFiles[idx] = newpath;
         }
     }

+ 19 - 8
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageServiceWebWorker.ts

@@ -168,7 +168,7 @@ export default class TypescriptLanguageServiceWebWorker {
                 case WorkerProcessTypes.GetDocTooltip:
                     this.handleGetDocTooltip(port, e.data);
                     break;
-                case ClientExtensionEventNames.ResourceSavedEvent:
+                case ClientExtensionEventNames.CodeSavedEvent:
                     this.handleSave(port, e.data);
                     break;
                 case ClientExtensionEventNames.ResourceRenamedEvent:
@@ -180,6 +180,9 @@ export default class TypescriptLanguageServiceWebWorker {
                 case ClientExtensionEventNames.ProjectUnloadedEvent:
                     this.handleProjectUnloaded(port);
                     break;
+                case WorkerProcessTypes.GetAnnotations:
+                    this.handleGetAnnotations(port, e.data);
+                    break;
             }
 
         }, false);
@@ -247,7 +250,8 @@ export default class TypescriptLanguageServiceWebWorker {
     }) {
         port.postMessage({ command: WorkerProcessTypes.Message, message: "Hello " + eventData.sender + " (port #" + this.connections + ")" });
         this.loadProjectFiles().then(() => {
-            this.languageService.compile([eventData.filename]);
+            let diagnostics = this.languageService.compile([eventData.filename]);
+            this.handleGetAnnotations(port, eventData);
         });
     }
 
@@ -314,7 +318,7 @@ export default class TypescriptLanguageServiceWebWorker {
         if (details) {
             let docs = details.displayParts.map(part => part.text).join("");
             if (details.documentation) {
-                docs += "<br/" + details.documentation.map(part => part.text).join("");
+                docs += "<p>" + details.documentation.map(part => part.text).join("") + "</p>";
             }
 
             message.docHTML = docs;
@@ -323,16 +327,23 @@ export default class TypescriptLanguageServiceWebWorker {
         port.postMessage(message);
     }
 
+    handleGetAnnotations(port: MessagePort, eventData: WorkerProcessTypes.GetAnnotationsMessageData) {
+        let message: WorkerProcessTypes.GetAnnotationsResponseMessageData = {
+            command: WorkerProcessTypes.AnnotationsUpdated,
+            annotations: this.languageService.getPreEmitWarnings(eventData.filename)
+        };
+
+        port.postMessage(message);
+    }
+
     /**
-     * Called when the file has been saved.
+     * Called when the file has been saved.  This will also send back annotations to the caller
      * @param  {MessagePort} port
      * @param  {WorkerProcessCommands.SaveMessageData} eventData
      */
     handleSave(port: MessagePort, eventData: WorkerProcessTypes.SaveMessageData) {
-        // let's reload the file
-        getFileResource(eventData.path).then((code: string) => {
-            this.languageService.updateProjectFile(eventData.path, code);
-        });
+        this.languageService.updateProjectFile(eventData.filename, eventData.code);
+        this.handleGetAnnotations(port, eventData);
     }
 
     /**

+ 11 - 4
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerProcessTypes.ts

@@ -28,6 +28,10 @@ export interface WorkerProcessMessageData {
     command: string;
 }
 
+export interface SaveMessageData extends WorkerProcessMessageData, Editor.EditorEvents.CodeSavedEvent { }
+export interface DeleteMessageData extends WorkerProcessMessageData, Editor.EditorEvents.DeleteResourceEvent {}
+export interface RenameMessageData extends WorkerProcessMessageData, Editor.EditorEvents.RenameResourceEvent {}
+
 export const GetCompletions = "COMPLETIONS";
 export const CompletionResponse = "COMPLETION_RESPONSE";
 export interface WordCompletion {
@@ -65,11 +69,14 @@ export interface GetDocTooltipResponseMessageData extends WorkerProcessMessageDa
     docHTML?: string;
 }
 
+export const GetAnnotations = "ANNOTATIONS";
+export const AnnotationsUpdated = "ANNOTATIONS_RESPONSE";
+export interface GetAnnotationsMessageData extends SaveMessageData {};
+export interface GetAnnotationsResponseMessageData extends WorkerProcessMessageData {
+    annotations: any[];
+}
+
 export const Connect = "HELO";
 export const Disconnect = "CLOSE";
 export const Message = "MESSAGE";
 export const Alert = "ALERT";
-
-export interface SaveMessageData extends WorkerProcessMessageData, Editor.EditorEvents.SaveResourceEvent { }
-export interface DeleteMessageData extends WorkerProcessMessageData, Editor.EditorEvents.DeleteResourceEvent {}
-export interface RenameMessageData extends WorkerProcessMessageData, Editor.EditorEvents.RenameResourceEvent {}

+ 16 - 0
Script/AtomicWebViewEditor/editor/editorCommands.ts

@@ -107,3 +107,19 @@ export function resourceDeleted(path: string) {
     };
     serviceLocator.sendEvent(ClientExtensionEventNames.ResourceDeletedEvent, data);
 }
+
+/**
+ * Called when a resource is saved
+ * @param  {string} path
+ * @param {string} fileExt
+ * @param {string} contents
+ */
+export function codeSaved(path: string, fileExt: string, contents: string) {
+    let data:Editor.EditorEvents.CodeSavedEvent = {
+        filename: path,
+        fileExt: fileExt,
+        editor: editor,
+        code: contents
+    };
+    serviceLocator.sendEvent(ClientExtensionEventNames.CodeSavedEvent, data);
+}

+ 18 - 4
Script/AtomicWebViewEditor/interop.ts

@@ -62,6 +62,9 @@ function atomicQueryPromise(message: any): Promise<{}> {
 export default class HostInteropType {
 
     private static _inst: HostInteropType = null;
+    private fileName: string = null;
+    private fileExt: string = null;
+
     static getInstance(): HostInteropType {
         if (HostInteropType._inst == null) {
             HostInteropType._inst = new HostInteropType();
@@ -90,9 +93,13 @@ export default class HostInteropType {
      */
     loadCode(codeUrl: string) {
         console.log("Load Code called for :" + codeUrl);
-        const fileExt = codeUrl.split(".").pop();
+        const fileExt = codeUrl.indexOf(".") != -1 ? codeUrl.split(".").pop() : "";
         const filename = codeUrl.replace("atomic://", "");
 
+        // Keep track of our filename
+        this.fileName = filename;
+        this.fileExt = fileExt;
+
         // go ahead and set the theme prior to pulling the file across
         editorCommands.configure(fileExt, filename);
 
@@ -108,10 +115,13 @@ export default class HostInteropType {
      * Save the contents of the editor
      * @return {Promise}
      */
-    saveCode(): Promise<{}> {
+    saveCode(): Promise<any> {
+        let source = editorCommands.getSourceText();
         return atomicQueryPromise({
             message: HostInteropType.EDITOR_SAVE_CODE,
-            payload: editorCommands.getSourceText()
+            payload: source
+        }).then(() => {
+            editorCommands.codeSaved(this.fileName, this.fileExt, source);
         });
     }
 
@@ -121,11 +131,14 @@ export default class HostInteropType {
      * @param  {string} fileContents
      * @return {Promise}
      */
-    saveFile(filename: string, fileContents: string): Promise<{}> {
+    saveFile(filename: string, fileContents: string): Promise<any> {
+        const fileExt = filename.indexOf(".") != -1 ? filename.split(".").pop() : "";
         return atomicQueryPromise({
             message: HostInteropType.EDITOR_SAVE_FILE,
             filename: filename,
             payload: fileContents
+        }).then(() => {
+            editorCommands.codeSaved(filename, fileExt, fileContents);
         });
     }
 
@@ -189,6 +202,7 @@ export default class HostInteropType {
      * @param  {string} newPath
      */
     resourceRenamed(path: string, newPath: string) {
+        this.fileName = newPath;
         editorCommands.resourceRenamed(path, newPath);
     }
 

+ 1 - 0
Script/AtomicWebViewEditor/tsconfig.json

@@ -38,6 +38,7 @@
         "../TypeScript/AtomicNET.d.ts",
         "../TypeScript/AtomicPlayer.d.ts",
         "../TypeScript/AtomicWork.d.ts",
+        "../TypeScript/duktape.d.ts",
         "../TypeScript/Editor.d.ts",
         "../TypeScript/EditorWork.d.ts",
         "../TypeScript/ToolCore.d.ts",

+ 2 - 2
Script/Packages/Atomic/Core.json

@@ -19,8 +19,8 @@
 
 		"Object" : [
 			"sendEvent(eventType:string, data?:Object);",
-			"subscribeToEvent(eventType:string, callback:(data:any)=>void);",
-			"subscribeToEvent(sender:AObject, eventType:string, callback:(data:any)=>void);"
+			"subscribeToEvent(eventType:string, callback:(data:any) => void);",
+			"subscribeToEvent(sender:AObject, eventType:string, callback:(data: any) => void);"
 		]
 	},
 	"haxe_decl" : {

+ 9 - 2
Script/Packages/Atomic/Scene.json

@@ -39,14 +39,21 @@
 			"getChildrenWithName(name:string, recursive?:boolean):Node[];",
 			"getChildrenWithComponent(componentType:string, recursive?:boolean):Node[];",
 			"getComponents(componentType?:string, recursive?:boolean):Component[];",
+			"getComponent<T extends Atomic.Component>(type: string): T;",
 			"getChildAtIndex(index:number):Node;",
 			"createJSComponent(name:string, args?:{});",
-			"getJSComponent(name:string, recursive?:boolean):JSComponent;",
+			"getJSComponent(name:string, recursive?:boolean): JSComponent;",
+			"getJSComponent<T extends Atomic.JSComponent>(name:string, recursive?:boolean): T;",
 			"createChildPrefab(childName:string, prefabPath:string):Node;",
-			"loadPrefab(prefabPath:string):boolean;"
+			"loadPrefab(prefabPath:string):boolean;",
+			"createComponent<T extends Atomic.Component>(type: string, mode?: CreateMode, id?: number): T;",
+			"getOrCreateComponent<T extends Atomic.Component>(type: string, mode?: CreateMode, id?: number): T;"
 		],
 		"Scene" : [
 			"getMainCamera():Camera;"
+		],
+		"Component": [
+		    "getComponent<T extends Atomic.Component>(type: string): T;"
 		]
 	},
 	"haxe_decl" : {

+ 1 - 1
Script/TypeScript/AtomicWork.d.ts

@@ -2,7 +2,7 @@
 /// <reference path="ToolCore.d.ts" />
 /// <reference path="Editor.d.ts" />
 /// <reference path="AtomicPlayer.d.ts" />
-
+/// <reference path="AtomicNET.d.ts" />
 
 declare module Atomic {
 

+ 53 - 12
Script/TypeScript/EditorWork.d.ts

@@ -5,7 +5,9 @@
 // license information: https://github.com/AtomicGameEngine/AtomicGameEngine
 //
 
+/// <reference path="Atomic.d.ts" />
 /// <reference path="Editor.d.ts" />
+/// <reference path="ToolCore.d.ts" />
 
 declare module Editor.EditorEvents {
 
@@ -76,6 +78,10 @@ declare module Editor.EditorEvents {
         code: string;
     }
 
+    export interface CodeSavedEvent extends EditorFileEvent {
+        code: string;
+    }
+
     export interface EditorCloseResourceEvent {
 
         editor: Editor.ResourceEditor;
@@ -141,7 +147,7 @@ declare module Editor.Extensions {
     /**
      * Base interface for any editor services.
      */
-    export interface EditorService {
+    export interface EditorServiceExtension {
         /**
          * Unique name of this service
          * @type {string}
@@ -156,6 +162,12 @@ declare module Editor.Extensions {
 
     }
 
+    /**
+     * Base Service Event Listener.  Attach descendents of these to an EditorServiceExtension
+     * to hook service events
+     */
+    export interface ServiceEventListener extends EditorServiceExtension { }
+
     interface EventDispatcher {
         /**
          * Send a custom event.  This can be used by services to publish custom events
@@ -181,13 +193,13 @@ declare module Editor.Extensions {
          * Loads a service into a service registry
          * @param  {EditorService} service
          */
-        loadService(service: EditorService): void;
+        loadService(service: EditorServiceExtension): void;
     }
 
     /**
      * Service registry interface for registering services
      */
-    export interface ServiceRegistry<T extends EditorService> {
+    export interface ServicesProvider<T extends ServiceEventListener> {
         registeredServices: T[];
 
         /**
@@ -195,6 +207,17 @@ declare module Editor.Extensions {
          * @param  {T}      service the service to register
          */
         register(service: T);
+        /**
+         * Removes a service from the registered services list for this type of service
+         * @param  {T}      service the service to unregister
+         */
+        unregister(service: T);
+    }
+}
+
+declare module Editor.Modal {
+    export interface ExtensionWindow extends Atomic.UIWindow {
+        hide();
     }
 }
 
@@ -205,28 +228,46 @@ declare module Editor.HostExtensions {
      * or by the editor itself.
      */
     export interface HostServiceLocator extends Editor.Extensions.ServiceLoader {
-        resourceServices: Editor.Extensions.ServiceRegistry<ResourceService>;
-        projectServices: Editor.Extensions.ServiceRegistry<ProjectService>;
+        resourceServices: ResourceServicesProvider;
+        projectServices: ProjectServicesProvider;
+        uiServices: UIServicesProvider;
     }
 
-    export interface HostEditorService extends Editor.Extensions.EditorService {
+    export interface HostEditorService extends Editor.Extensions.EditorServiceExtension {
         /**
          * Called by the service locator at load time
          */
-        initialize(serviceLocator: Editor.Extensions.ServiceLoader);
+        initialize(serviceLocator: HostServiceLocator);
     }
 
-    export interface ResourceService extends Editor.Extensions.EditorService {
+    export interface ResourceServicesEventListener extends Editor.Extensions.ServiceEventListener {
         save?(ev: EditorEvents.SaveResourceEvent);
         delete?(ev: EditorEvents.DeleteResourceEvent);
         rename?(ev: EditorEvents.RenameResourceEvent);
     }
+    export interface ResourceServicesProvider extends Editor.Extensions.ServicesProvider<ResourceServicesEventListener> { }
 
-    export interface ProjectService extends Editor.Extensions.EditorService {
+    export interface ProjectServicesEventListener extends Editor.Extensions.ServiceEventListener {
         projectUnloaded?();
         projectLoaded?(ev: EditorEvents.LoadProjectEvent);
         playerStarted?();
     }
+    export interface ProjectServicesProvider extends Editor.Extensions.ServicesProvider<ProjectServicesEventListener> { }
+
+    export interface UIServicesEventListener extends Editor.Extensions.ServiceEventListener {
+        menuItemClicked?(refid: string): boolean;
+        projectContextItemClicked?(asset: ToolCore.Asset, refid: string): boolean;
+        hierarchyContextItemClicked?(node: Atomic.Node, refid: string): boolean;
+    }
+    export interface UIServicesProvider extends Editor.Extensions.ServicesProvider<UIServicesEventListener> {
+        createPluginMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource;
+        removePluginMenuItemSource(id: string);
+        createHierarchyContextMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource;
+        removeHierarchyContextMenuItemSource(id: string);
+        createProjectContextMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource;
+        removeProjectContextMenuItemSource(id: string);
+        showModalWindow(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void): Editor.Modal.ExtensionWindow;
+    }
 }
 
 /**
@@ -242,17 +283,17 @@ declare module Editor.ClientExtensions {
         getHostInterop(): HostInterop;
     }
 
-    export interface ClientEditorService extends Editor.Extensions.EditorService {
+    export interface ClientEditorService extends Editor.Extensions.EditorServiceExtension {
         /**
          * Called by the service locator at load time
          */
         initialize(serviceLocator: ClientServiceLocator);
     }
 
-    export interface WebViewService extends Editor.Extensions.EditorService {
+    export interface WebViewService extends Editor.Extensions.EditorServiceExtension {
         configureEditor?(ev: EditorEvents.EditorFileEvent);
         codeLoaded?(ev: EditorEvents.CodeLoadedEvent);
-        save?(ev: EditorEvents.SaveResourceEvent);
+        save?(ev: EditorEvents.CodeSavedEvent);
         delete?(ev: EditorEvents.DeleteResourceEvent);
         rename?(ev: EditorEvents.RenameResourceEvent);
         projectUnloaded?();

+ 17 - 0
Script/TypeScript/duktape.d.ts

@@ -0,0 +1,17 @@
+// Duktape built-ins
+
+// extracted from lib.d.ts
+declare interface Console {
+    log(message?: any, ...optionalParams: any[]): void;
+}
+
+declare var console: Console;
+
+// Duktape require isn't recognized as a function, but can be used as one
+declare function require(filename: string): any;
+
+declare interface DuktapeModule {
+    modSearch(id: string, require, exports, module);
+}
+
+declare var Duktape: DuktapeModule;

+ 3 - 0
Script/tsconfig.json

@@ -25,6 +25,7 @@
         "./AtomicEditor/editor/EditorEvents.ts",
         "./AtomicEditor/editor/EditorLicense.ts",
         "./AtomicEditor/editor/Preferences.ts",
+        "./AtomicEditor/hostExtensions/coreExtensions/ProjectBasedExtensionLoader.ts",
         "./AtomicEditor/hostExtensions/HostExtensionServices.ts",
         "./AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts",
         "./AtomicEditor/hostExtensions/ServiceLocator.ts",
@@ -75,6 +76,7 @@
         "./AtomicEditor/ui/modal/build/platforms/WebSettingsWidget.ts",
         "./AtomicEditor/ui/modal/build/platforms/WindowsSettingsWidget.ts",
         "./AtomicEditor/ui/modal/CreateProject.ts",
+        "./AtomicEditor/ui/modal/ExtensionWindow.ts",
         "./AtomicEditor/ui/modal/license/ActivationSuccessWindow.ts",
         "./AtomicEditor/ui/modal/license/ActivationWindow.ts",
         "./AtomicEditor/ui/modal/license/EULAWindow.ts",
@@ -98,6 +100,7 @@
         "./TypeScript/AtomicNET.d.ts",
         "./TypeScript/AtomicPlayer.d.ts",
         "./TypeScript/AtomicWork.d.ts",
+        "./TypeScript/duktape.d.ts",
         "./TypeScript/Editor.d.ts",
         "./TypeScript/EditorWork.d.ts",
         "./TypeScript/ToolCore.d.ts",

+ 66 - 138
Source/Atomic/Engine/EngineConfig.cpp

@@ -31,38 +31,7 @@
 namespace Atomic
 {
 
-VariantMap EngineConfig::engineConfig_;
-String EngineConfig::engineConfigFilename_;
-
-bool EngineConfig::GetBoolValue(const JSONValue& jvalue, bool defaultValue)
-{
-    bool value = defaultValue;
-
-    if (jvalue.IsBool())
-        value = jvalue.GetBool();
-
-    return value;
-}
-
-int EngineConfig::GetIntValue(const JSONValue& jvalue, int defaultValue)
-{
-    int value = defaultValue;
-
-    if (jvalue.IsNumber())
-        value = jvalue.GetInt();
-
-    return value;
-}
-
-String EngineConfig::GetStringValue(const JSONValue& jvalue, const String& defaultValue)
-{
-    String value = defaultValue;
-
-    if (jvalue.IsString())
-        value = jvalue.GetString();
-
-    return value;
-}
+EngineConfig EngineConfig::engineConfig_;
 
 bool EngineConfig::LoadEngineConfig(const JSONValue& jengine)
 {
@@ -75,11 +44,11 @@ bool EngineConfig::LoadEngineConfig(const JSONValue& jengine)
         const JSONValue& jvalue = i->second_;
 
         if (key == "workerthreads")
-            engineConfig_["WorkerThreads"] = GetBoolValue(jvalue, true);
+            valueMap_["WorkerThreads"] = GetBoolValue(jvalue, true);
         else if (key == "logquiet")
-            engineConfig_["LogQuiet"] = GetBoolValue(jvalue, false);
+            valueMap_["LogQuiet"] = GetBoolValue(jvalue, false);
         else if (key == "loglevel")
-            engineConfig_["LogLevel"] = GetIntValue(jvalue, 1);
+            valueMap_["LogLevel"] = GetIntValue(jvalue, 1);
     }
 
     return true;
@@ -96,74 +65,74 @@ bool EngineConfig::LoadGraphicsConfig(const JSONValue& jgraphics)
         const JSONValue& jvalue = i->second_;
 
         if (key == "headless")
-            engineConfig_["Headless"] = GetBoolValue(jvalue, false);
+            valueMap_["Headless"] = GetBoolValue(jvalue, false);
         else if (key == "framelimiter")
-            engineConfig_["FrameLimiter"] = GetBoolValue(jvalue, true);
+            valueMap_["FrameLimiter"] = GetBoolValue(jvalue, true);
         else if (key == "flushgpu")
-            engineConfig_["FlushGPU"] = GetBoolValue(jvalue, false);
+            valueMap_["FlushGPU"] = GetBoolValue(jvalue, false);
         else if (key == "forcegl2")
-            engineConfig_["ForceGL2"] = GetBoolValue(jvalue, false);
+            valueMap_["ForceGL2"] = GetBoolValue(jvalue, false);
         else if (key == "orientations")
-            engineConfig_["Orientations"] = GetStringValue(jvalue, "LandscapeLeft LandscapeRight");
+            valueMap_["Orientations"] = GetStringValue(jvalue, "LandscapeLeft LandscapeRight");
         else if (key == "vsync")
-            engineConfig_["VSync"] = GetBoolValue(jvalue, false);
+            valueMap_["VSync"] = GetBoolValue(jvalue, false);
         else if (key == "triplebuffer")
-            engineConfig_["TripleBuffer"] = GetBoolValue(jvalue, false);
+            valueMap_["TripleBuffer"] = GetBoolValue(jvalue, false);
         else if (key == "multisample")
-            engineConfig_["Multisample"] = GetIntValue(jvalue, 1);
+            valueMap_["Multisample"] = GetIntValue(jvalue, 1);
         else if (key == "renderpath")
         {
             String renderPath = GetStringValue(jvalue, "forward").ToLower();
 
             if (renderPath == "forward")
-                engineConfig_["RenderPath"] = "RenderPaths/Forward.xml";
+                valueMap_["RenderPath"] = "RenderPaths/Forward.xml";
             else if (renderPath == "prepass")
-                engineConfig_["RenderPath"] = "RenderPaths/Prepass.xml";
+                valueMap_["RenderPath"] = "RenderPaths/Prepass.xml";
             else if (renderPath == "deferred")
-                engineConfig_["RenderPath"] = "RenderPaths/Deferred.xml";
+                valueMap_["RenderPath"] = "RenderPaths/Deferred.xml";
         }
         else if (key == "shadows")
-            engineConfig_["Shadows"] = GetBoolValue(jvalue, true);
+            valueMap_["Shadows"] = GetBoolValue(jvalue, true);
         else if (key == "lowqualityshadows")
-            engineConfig_["LowQualityShadows"] = GetBoolValue(jvalue, false);
+            valueMap_["LowQualityShadows"] = GetBoolValue(jvalue, false);
         else if (key == "materialquality")
         {
             String quality = GetStringValue(jvalue, "high").ToLower();
 
             if (quality == "high")
-                engineConfig_["MaterialQuality"] = QUALITY_HIGH;
+                valueMap_["MaterialQuality"] = QUALITY_HIGH;
             else if (quality == "medium")
-                engineConfig_["MaterialQuality"] = QUALITY_MEDIUM;
+                valueMap_["MaterialQuality"] = QUALITY_MEDIUM;
             else if (quality == "low")
-                engineConfig_["MaterialQuality"] = QUALITY_LOW;
+                valueMap_["MaterialQuality"] = QUALITY_LOW;
         }
         else if (key == "texturequality")
         {
             String quality = GetStringValue(jvalue, "high").ToLower();
 
             if (quality == "high")
-                engineConfig_["TextureQuality"] = QUALITY_HIGH;
+                valueMap_["TextureQuality"] = QUALITY_HIGH;
             else if (quality == "medium")
-                engineConfig_["TextureQuality"] = QUALITY_MEDIUM;
+                valueMap_["TextureQuality"] = QUALITY_MEDIUM;
             else if (quality == "low")
-                engineConfig_["TextureQuality"] = QUALITY_LOW;
+                valueMap_["TextureQuality"] = QUALITY_LOW;
         }
         else if (key == "texturefiltermode")
         {
             String mode = GetStringValue(jvalue, "trilinear").ToLower();
 
             if (mode == "trilinear")
-                engineConfig_["TextureFilterMode"] = FILTER_TRILINEAR;
+                valueMap_["TextureFilterMode"] = FILTER_TRILINEAR;
             else if (mode == "bilinear")
-                engineConfig_["TextureFilterMode"] = FILTER_BILINEAR;
+                valueMap_["TextureFilterMode"] = FILTER_BILINEAR;
             else if (mode == "nearest")
-                engineConfig_["TextureFilterMode"] = FILTER_NEAREST;
+                valueMap_["TextureFilterMode"] = FILTER_NEAREST;
             else if (mode == "anisotropic")
-                engineConfig_["TextureFilterMode"] = FILTER_ANISOTROPIC;
+                valueMap_["TextureFilterMode"] = FILTER_ANISOTROPIC;
         }
         else if (key == "textureanisotropy")
         {
-            engineConfig_["TextureAnisotropy"] = GetIntValue(jvalue, 4);
+            valueMap_["TextureAnisotropy"] = GetIntValue(jvalue, 4);
         }
 
 
@@ -183,21 +152,21 @@ bool EngineConfig::LoadWindowConfig(const JSONValue& jwindow)
         const JSONValue& jvalue = i->second_;
 
         if (key == "title")
-            engineConfig_["WindowTitle"] = GetStringValue(jvalue, "Atomic");
+            valueMap_["WindowTitle"] = GetStringValue(jvalue, "Atomic");
         else if (key == "fullscreen")
-            engineConfig_["FullScreen"] = GetBoolValue(jvalue, false);
+            valueMap_["FullScreen"] = GetBoolValue(jvalue, false);
         else if (key == "borderless")
-            engineConfig_["Borderless"] = GetBoolValue(jvalue, false);
+            valueMap_["Borderless"] = GetBoolValue(jvalue, false);
         else if (key == "resizable")
-            engineConfig_["WindowResizable"] = GetBoolValue(jvalue, false);
+            valueMap_["WindowResizable"] = GetBoolValue(jvalue, false);
         else if (key == "width")
-            engineConfig_["WindowWidth"] = GetIntValue(jvalue, false);
+            valueMap_["WindowWidth"] = GetIntValue(jvalue, false);
         else if (key == "height")
-            engineConfig_["WindowHeight"] = GetIntValue(jvalue, false);
+            valueMap_["WindowHeight"] = GetIntValue(jvalue, false);
         else if (key == "positionx")
-            engineConfig_["WindowPositionX"] = GetIntValue(jvalue, false);
+            valueMap_["WindowPositionX"] = GetIntValue(jvalue, false);
         else if (key == "positiony")
-            engineConfig_["WindowPositionY"] = GetIntValue(jvalue, false);
+            valueMap_["WindowPositionY"] = GetIntValue(jvalue, false);
 
     }
 
@@ -216,15 +185,15 @@ bool EngineConfig::LoadSoundConfig(const JSONValue& jsound)
         const JSONValue& jvalue = i->second_;
 
         if (key == "enabled")
-            engineConfig_["Sound"] = GetBoolValue(jvalue, true);
+            valueMap_["Sound"] = GetBoolValue(jvalue, true);
         else if (key == "interpolation")
-            engineConfig_["SoundInterpolation"] = GetBoolValue(jvalue, true);
+            valueMap_["SoundInterpolation"] = GetBoolValue(jvalue, true);
         else if (key == "stereo")
-            engineConfig_["SoundStereo"] = GetBoolValue(jvalue, true);
+            valueMap_["SoundStereo"] = GetBoolValue(jvalue, true);
         else if (key == "bufferms")
-            engineConfig_["SoundBuffer"] = GetIntValue(jvalue, 100);
+            valueMap_["SoundBuffer"] = GetIntValue(jvalue, 100);
         else if (key == "mixrate")
-            engineConfig_["SoundMixRate"] = GetIntValue(jvalue, 44100);
+            valueMap_["SoundMixRate"] = GetIntValue(jvalue, 44100);
 
     }
 
@@ -243,13 +212,34 @@ bool EngineConfig::LoadInputConfig(const JSONValue& jinput)
         const JSONValue& jvalue = i->second_;
 
         if (key == "touchemulation")
-            engineConfig_["TouchEmulation"] = GetBoolValue(jvalue, false);
+            valueMap_["TouchEmulation"] = GetBoolValue(jvalue, false);
     }
 
     return true;
 
 }
 
+bool EngineConfig::LoadWebViewConfig(const JSONValue& jwebview)
+{
+    if (!jwebview.IsObject())
+        return false;
+
+    for (JSONObject::ConstIterator i = jwebview.Begin(); i != jwebview.End(); ++i)
+    {
+        String key = i->first_.ToLower();
+        const JSONValue& jvalue = i->second_;
+
+        if (key == "useragent")
+            valueMap_["WebViewUserAgent"] = GetStringValue(jvalue, String::EMPTY);
+        else if (key == "productversion")
+            valueMap_["WebViewProductVersion"] = GetStringValue(jvalue, String::EMPTY);
+        else if (key == "debugPort")
+            valueMap_["WebViewDebugPort"] = GetIntValue(jvalue, 8080);
+    }
+
+    return true;
+
+}
 
 bool EngineConfig::LoadDesktopConfig(JSONValue root)
 {
@@ -278,74 +268,12 @@ bool EngineConfig::LoadDesktopConfig(JSONValue root)
     if (jinput.IsObject())
         LoadInputConfig(jinput);
 
-    return true;
-}
-
-bool EngineConfig::LoadFromJSON(const String& json)
-{
-    engineConfig_.Clear();
-
-    JSONValue jroot;
+    const JSONValue& jwebview = jdesktop["webview"];
+    if (jwebview.IsObject())
+        LoadWebViewConfig(jwebview);
 
-    if (!JSONFile::ParseJSON(json, jroot))
-    {
-        LOGERRORF("EngineConfig::LoadFromJSON - Unable to parse config file JSON: %s", engineConfigFilename_.CString());
-        return false;
-    }
-
-    if (!jroot.IsObject())
-        return false;
-
-    if (!LoadDesktopConfig(jroot))
-        return false;
 
     return true;
 }
 
-bool EngineConfig::LoadFromFile(Context *context, const String& filename)
-{
-
-    FileSystem* fileSystem = context->GetSubsystem<FileSystem>();
-
-    if (!fileSystem->FileExists(filename))
-        return false;
-
-    engineConfigFilename_ = filename;
-
-    SharedPtr<File> file(new File(context));
-
-    if (!file->Open(filename))
-    {
-        LOGERRORF("EngineConfig::LoadFromFile - Unable to open config file %s", filename.CString());
-        return false;
-    }
-
-    String json;
-    file->ReadText(json);
-
-    return LoadFromJSON(json);
-}
-
-void EngineConfig::ApplyConfig(VariantMap& settings, bool overwrite)
-{
-    VariantMap::ConstIterator itr = engineConfig_.Begin();
-    if (overwrite)
-    { 
-        while (itr != engineConfig_.End())
-        {
-            settings[itr->first_] = itr->second_;
-            itr++;
-        }
-    }
-    else
-    {
-        while (itr != engineConfig_.End())
-        {
-            settings.InsertNew(itr->first_, itr->second_);
-            itr++;
-        }
-    }
-
-}
-
 }

+ 14 - 18
Source/Atomic/Engine/EngineConfig.h

@@ -24,6 +24,7 @@
 
 #include "../Core/Variant.h"
 #include "../Resource/JSONValue.h"
+#include "../Resource/Configuration.h"
 
 namespace Atomic
 {
@@ -31,36 +32,31 @@ namespace Atomic
 class Context;
 
 /// Atomic engine configuration
-class EngineConfig
+class EngineConfig :
+    Configuration
 {
 
 public:
 
-    static bool LoadFromFile(Context* context, const String& filename);
-    static bool LoadFromJSON(const String& json);
+    static bool LoadFromFile(Context* context, const String& filename) { return engineConfig_.Configuration::LoadFromFile(context, filename); }
+    static bool LoadFromJSON(const String& json) { return engineConfig_.Configuration::LoadFromJSON(json); }
 
     /// Apply the configuration to a setting variant map, values that exist will not be overriden
-    static void ApplyConfig(VariantMap& settings, bool overwrite = false);
-
-    static const VariantMap& GetConfig() { return engineConfig_; }
+    static void ApplyConfig(VariantMap& settings, bool overwrite = false) { return engineConfig_.Configuration::ApplyConfig(settings, overwrite); }
 
 private:
 
-    static bool LoadDesktopConfig(JSONValue root);
-    static bool LoadGraphicsConfig(const JSONValue& jgraphics);
-    static bool LoadWindowConfig(const JSONValue& jwindow);
-    static bool LoadSoundConfig(const JSONValue& jsound);
-    static bool LoadInputConfig(const JSONValue& jinput);
-
-    static bool GetBoolValue(const JSONValue& jvalue, bool defaultValue);
-    static int GetIntValue(const JSONValue& jvalue, int defaultValue);
-    static String GetStringValue(const JSONValue& jvalue, const String& defaultValue);
+    virtual bool LoadDesktopConfig(JSONValue root);
 
-    static bool LoadEngineConfig(const JSONValue& jengine);
+    bool LoadWebViewConfig(const JSONValue& jwebview);
+    bool LoadGraphicsConfig(const JSONValue& jgraphics);
+    bool LoadWindowConfig(const JSONValue& jwindow);
+    bool LoadSoundConfig(const JSONValue& jsound);
+    bool LoadInputConfig(const JSONValue& jinput);
 
-    static VariantMap engineConfig_;
-    static String engineConfigFilename_;
+    bool LoadEngineConfig(const JSONValue& jengine);
 
+    static EngineConfig engineConfig_;
 };
 
 }

+ 8 - 0
Source/Atomic/Graphics/Direct3D9/D3D9ShaderVariation.cpp

@@ -31,7 +31,10 @@
 #include "../../IO/Log.h"
 #include "../../Resource/ResourceCache.h"
 
+#ifdef ATOMIC_D3DCOMPILER_ENABLED
 #include <d3dcompiler.h>
+#endif
+
 #include <MojoShader/mojoshader.h>
 
 #include "../../DebugNew.h"
@@ -220,6 +223,10 @@ bool ShaderVariation::LoadByteCode(PODVector<unsigned>& byteCode, const String&
 
 bool ShaderVariation::Compile(PODVector<unsigned>& byteCode)
 {
+
+#ifndef ATOMIC_D3DCOMPILER_ENABLED
+    return false;
+#else
     const String& sourceCode = owner_->GetSourceCode(type_);
     Vector<String> defines = defines_.Split(' ');
 
@@ -306,6 +313,7 @@ bool ShaderVariation::Compile(PODVector<unsigned>& byteCode)
         errorMsgs->Release();
 
     return !byteCode.Empty();
+#endif
 }
 
 void ShaderVariation::ParseParameters(unsigned char* bufData, unsigned bufSize)

+ 29 - 0
Source/Atomic/IPC/IPC.cpp

@@ -33,6 +33,13 @@
 #include "IPC.h"
 #include "IPCEvents.h"
 
+#if defined(ATOMIC_PLATFORM_WINDOWS)
+
+#include <windows.h>
+#undef PostMessage
+
+#endif
+
 namespace Atomic
 {
 
@@ -40,6 +47,28 @@ IPC::IPC(Context* context) : Object(context),
     workerChannelID_(0)
 {
     SubscribeToEvent(E_BEGINFRAME, HANDLER(IPC, HandleBeginFrame));
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+
+    jobHandle_ = CreateJobObject(NULL, NULL);
+    if (!jobHandle_)
+    {
+        LOGERROR("IPC::IPC - Unable to create IPC job");
+    }
+    else
+    {
+        JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
+
+        // Configure all child processes associated with the job to terminate when main process is closed
+        jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+        if (0 == SetInformationJobObject(jobHandle_, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)))
+        {
+            LOGERROR("IPC::IPC - Unable set job information");
+            jobHandle_ = 0;
+        }
+    }
+
+#endif
 }
 
 IPC::~IPC()

+ 8 - 0
Source/Atomic/IPC/IPC.h

@@ -65,6 +65,10 @@ public:
     void SendEventToBroker(StringHash eventType);
     void SendEventToBroker(StringHash eventType, VariantMap& eventData);
 
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    IPCHandle GetJobHandle() const { return jobHandle_; }
+#endif
+
 private:
 
     // if non-zero we're a worked and this is out broker's channel id
@@ -82,6 +86,10 @@ private:
     // valid on child
     SharedPtr<IPCWorker> worker_;
 
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    IPCHandle jobHandle_;
+#endif
+
 };
 
 }

+ 14 - 0
Source/Atomic/IPC/IPCWindows.cpp

@@ -29,8 +29,11 @@
 #include <string>
 
 #include "../Core/Timer.h"
+#include "../IO/Log.h"
 #include "IPCWindows.h"
 
+#include "IPC.h"
+
 typedef std::wstring IPCWString;
 
 namespace Atomic
@@ -303,6 +306,17 @@ bool IPCProcess::Launch(const String& command, const Vector<String>& args, const
         return false;
     }
 
+    IPC* ipc = GetSubsystem<IPC>();
+    IPCHandle jobHandle = ipc->GetJobHandle();
+
+    if (jobHandle)
+    {
+        if (0 == AssignProcessToJobObject(jobHandle, pi.hProcess))
+        {
+            LOGERROR("IPCProcess::Launch - unable to assign job");
+        }
+    }
+
     pid_ = pi.hProcess;
     ::CloseHandle(pi.hThread);
 

+ 1 - 1
Source/Atomic/IPC/IPCWindows.h

@@ -132,7 +132,7 @@ private:
 
     IPCHandle pid_;
     IPCHandle clientRead_;
-    IPCHandle clientWrite_;
+    IPCHandle clientWrite_;    
 };
 
 }

+ 6 - 0
Source/Atomic/IPC/IPCWorker.cpp

@@ -101,10 +101,16 @@ void IPCWorker::ThreadFunction()
 {
     while (shouldRun_)
     {
+
+// On windows we use a job object to control process lifetime, we don't have a 
+// parent pid (these change and are reused on Windows, so we would need to DuplicateHandle and pass
+// to child on command line
+#ifndef ATOMIC_PLATFORM_WINDOWS
         if (!otherProcess_->IsRunning())
         {
             break;
         }
+#endif
 
         if (!Receive())
         {

+ 149 - 0
Source/Atomic/Resource/Configuration.cpp

@@ -0,0 +1,149 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+//
+// 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.
+//
+
+
+#include "../Core/Context.h"
+#include "../IO/Log.h"
+#include "../IO/File.h"
+#include "../IO/FileSystem.h"
+#include "../Resource/JSONFile.h"
+#include "../Graphics/GraphicsDefs.h"
+#include "Configuration.h"
+
+namespace Atomic
+{
+
+    bool Configuration::GetBoolValue(const JSONValue& jvalue, bool defaultValue)
+    {
+        bool value = defaultValue;
+
+        if (jvalue.IsBool())
+            value = jvalue.GetBool();
+
+        return value;
+    }
+
+    int Configuration::GetIntValue(const JSONValue& jvalue, int defaultValue)
+    {
+        int value = defaultValue;
+
+        if (jvalue.IsNumber())
+            value = jvalue.GetInt();
+
+        return value;
+    }
+
+    String Configuration::GetStringValue(const JSONValue& jvalue, const String& defaultValue)
+    {
+        String value = defaultValue;
+
+        if (jvalue.IsString())
+            value = jvalue.GetString();
+
+        return value;
+    }
+
+
+    Configuration::Configuration() :
+        isLoaded_(false)
+    {
+    }
+
+    bool Configuration::LoadFromFile(Context *context, const String& filename)
+    {
+
+        FileSystem* fileSystem = context->GetSubsystem<FileSystem>();
+
+        if (!fileSystem->FileExists(filename))
+            return false;
+
+        filename_ = filename;
+
+        SharedPtr<File> file(new File(context));
+
+        if (!file->Open(filename))
+        {
+            LOGERRORF("Configuration::LoadFromFile - Unable to open config file %s", filename.CString());
+            return false;
+        }
+
+        String json;
+        file->ReadText(json);
+
+        return LoadFromJSON(json);
+    }
+
+    void Configuration::ApplyConfig(VariantMap& settings, bool overwrite)
+    {
+        if (!isLoaded_) {
+            LOGERROR("Configuration::ApplyConfig - Applying a config that has not yet been populated");
+            return;
+        }
+
+        VariantMap::ConstIterator itr = valueMap_.Begin();
+        if (overwrite)
+        {
+            while (itr != valueMap_.End())
+            {
+                settings[itr->first_] = itr->second_;
+                itr++;
+            }
+        }
+        else
+        {
+            while (itr != valueMap_.End())
+            {
+                settings.InsertNew(itr->first_, itr->second_);
+                itr++;
+            }
+        }
+
+    }
+
+    bool Configuration::LoadFromJSON(const String& json)
+    {
+        Clear();
+
+        JSONValue jroot;
+
+        if (!JSONFile::ParseJSON(json, jroot))
+        {
+            LOGERRORF("Configuration::LoadFromJSON - Unable to parse config file JSON: %s", filename_.CString());
+            return false;
+        }
+
+        if (!jroot.IsObject())
+            return false;
+
+        if (!LoadDesktopConfig(jroot))
+            return false;
+
+        isLoaded_ = true;
+        return true;
+    }
+
+    void Configuration::Clear()
+    {
+        valueMap_.Clear();
+    }
+
+}

+ 66 - 0
Source/Atomic/Resource/Configuration.h

@@ -0,0 +1,66 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+//
+// 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.
+//
+
+#pragma once
+
+#include "../Core/Variant.h"
+#include "JSONValue.h"
+
+namespace Atomic
+{
+
+class Context;
+
+/// Configuration base class for mapping JSON setting files to VariantMap configuration settings
+class Configuration
+{
+public:
+
+    Configuration();
+
+    bool LoadFromFile(Context* context, const String& filename);
+    bool LoadFromJSON(const String& json);
+    void Clear();
+
+    /// Apply the configuration to a setting variant map, values that exist will not be overriden
+    void ApplyConfig(VariantMap& settings, bool overwrite = false);
+
+    const VariantMap& GetConfig() { return valueMap_; }
+    const bool IsLoaded() const { return isLoaded_; }
+
+protected:
+    static bool GetBoolValue(const JSONValue& jvalue, bool defaultValue);
+    static int GetIntValue(const JSONValue& jvalue, int defaultValue);
+    static String GetStringValue(const JSONValue& jvalue, const String& defaultValue);
+
+    virtual bool LoadDesktopConfig(JSONValue root) { return true; };
+
+    VariantMap valueMap_;
+
+private:
+    String filename_;
+    bool isLoaded_;
+};
+}
+
+
+

+ 153 - 0
Source/Atomic/Resource/Image.cpp

@@ -36,6 +36,10 @@
 
 #include "../DebugNew.h"
 
+#ifdef ATOMIC_PLATFORM_DESKTOP
+#include <libsquish/squish.h>
+#endif
+
 extern "C" unsigned char* stbi_write_png_to_mem(unsigned char* pixels, int stride_bytes, int x, int y, int n, int* out_len);
 
 #ifndef MAKEFOURCC
@@ -79,6 +83,25 @@ static const unsigned DDS_DXGI_FORMAT_BC2_UNORM_SRGB = 75;
 static const unsigned DDS_DXGI_FORMAT_BC3_UNORM = 77;
 static const unsigned DDS_DXGI_FORMAT_BC3_UNORM_SRGB = 78;
 
+// ATOMIC BEGIN
+static const unsigned DDSD_CAPS = 0x00000001;
+static const unsigned DDSD_HEIGHT = 0x00000002;
+static const unsigned DDSD_WIDTH = 0x00000004;
+static const unsigned DDSD_PITCH = 0x00000008;
+static const unsigned DDSD_PIXELFORMAT = 0x00001000;
+static const unsigned DDSD_MIPMAPCOUNT = 0x00020000;
+static const unsigned DDSD_LINEARSIZE = 0x00080000;
+static const unsigned DDSD_DEPTH = 0x00800000;
+
+static const unsigned DDPF_ALPHAPIXELS = 0x00000001;
+static const unsigned DDPF_ALPHA = 0x00000002;
+static const unsigned DDPF_FOURCC = 0x00000004;
+static const unsigned DDPF_PALETTEINDEXED8 = 0x00000020;
+static const unsigned DDPF_RGB = 0x00000040;
+static const unsigned DDPF_LUMINANCE = 0x00020000;
+static const unsigned DDPF_NORMAL = 0x80000000;  // nvidia specific
+// ATOMIC END
+
 namespace Atomic
 {
 
@@ -1211,6 +1234,136 @@ bool Image::SaveJPG(const String& fileName, int quality) const
         return false;
 }
 
+bool Image::SaveDDS(const String& fileName) const
+{
+#if !defined(ATOMIC_PLATFORM_DESKTOP)
+
+    LOGERRORF("Image::SaveDDS - Unsupported on current platform: %s", fileName.CString());
+    return false;
+
+#else
+    PROFILE(SaveImageDDS);
+
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+    if (fileSystem && !fileSystem->CheckAccess(GetPath(fileName)))
+    {
+        LOGERROR("Access denied to " + fileName);
+        return false;
+    }
+
+    if (IsCompressed())
+    {
+        LOGERROR("Can not save compressed image to DDS");
+        return false;
+    }
+
+    if (data_)
+    {
+        // #623 BEGIN TODO: Should have an abstract ImageReader and ImageWriter classes
+        // with subclasses for particular image output types. Also should have image settings in the image meta.
+        // ImageReader/Writers should also support a progress callback so UI can be updated.
+
+        if (!width_ || !height_)
+        {
+            LOGERRORF("Attempting to save zero width/height DDS to %s", fileName.CString());
+            return false;
+        }
+
+        squish::u8 *inputData = (squish::u8 *) data_.Get();
+
+        SharedPtr<Image> tempImage;
+
+        // libsquish expects 4 channel RGBA, so create a temporary image if necessary
+        if (components_ != 4)
+        {
+            if (components_ != 3)
+            {
+                LOGERROR("Can only save images with 3 or 4 components to DDS");
+                return false;
+            }
+
+            tempImage = new Image(context_);
+            tempImage->SetSize(width_, height_, 4);
+
+            squish::u8* srcBits = (squish::u8*) data_;
+            squish::u8* dstBits = (squish::u8*) tempImage->data_;
+
+            for (unsigned i = 0; i < (unsigned) width_ * (unsigned) height_; i++)
+            {
+                *dstBits++ = *srcBits++;
+                *dstBits++ = *srcBits++;
+                *dstBits++ = *srcBits++;
+                *dstBits++ = 255;
+            }
+
+            inputData = (squish::u8 *) tempImage->data_.Get();
+        }
+        
+        unsigned squishFlags = HasAlphaChannel() ? squish::kDxt5 : squish::kDxt1;
+
+        // Use a slow but high quality colour compressor (the default). (TODO: expose other settings as parameter)
+        squishFlags |= squish::kColourClusterFit;
+
+        unsigned storageRequirements = (unsigned) squish::GetStorageRequirements( width_, height_,  squishFlags);
+
+        SharedArrayPtr<unsigned char> compressedData(new unsigned char[storageRequirements]);
+
+        squish::CompressImage(inputData, width_, height_, compressedData.Get(), squishFlags);
+
+        DDSurfaceDesc2 sdesc;
+
+        if (sizeof(sdesc) != 124)
+        {
+            LOGERROR("Image::SaveDDS - sizeof(DDSurfaceDesc2) != 124");
+            return false;
+        }
+        memset(&sdesc, 0, sizeof(sdesc));
+        sdesc.dwSize_ = 124;
+        sdesc.dwFlags_ = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
+        sdesc.dwWidth_ = width_;
+        sdesc.dwHeight_ = height_;
+
+        sdesc.ddpfPixelFormat_.dwSize_ = 32;        
+        sdesc.ddpfPixelFormat_.dwFlags_ = DDPF_FOURCC | (HasAlphaChannel() ? DDPF_ALPHAPIXELS : 0);
+        sdesc.ddpfPixelFormat_.dwFourCC_ = HasAlphaChannel() ? FOURCC_DXT5 : FOURCC_DXT1;
+
+        SharedPtr<File> dest(new File(context_, fileName, FILE_WRITE));
+
+        if (!dest->IsOpen())
+        {
+            LOGERRORF("Failed to open DXT image file for writing %s", fileName.CString());
+            return false;
+        }
+        else
+        {
+
+            dest->Write((void*)"DDS ", 4);
+            dest->Write((void*)&sdesc, sizeof(sdesc));
+
+            bool success = dest->Write(compressedData.Get(), storageRequirements) == storageRequirements;
+
+            if (!success)
+                LOGERRORF("Failed to write image to DXT, file size mismatch %s", fileName.CString());
+
+            return success;
+        }        
+        // #623 END TODO
+    }    
+#endif
+
+    return false;
+}
+
+bool Image::IsPOT() const
+{
+    return IsPowerOfTwo(width_) && IsPowerOfTwo(height_);
+}
+
+bool Image::HasAlphaChannel() const
+{
+    return components_ > 3;
+}
+
 Color Image::GetPixel(int x, int y) const
 {
     return GetPixel(x, y, 0);

+ 6 - 0
Source/Atomic/Resource/Image.h

@@ -138,12 +138,18 @@ public:
     bool SaveTGA(const String& fileName) const;
     /// Save in JPG format with compression quality. Return true if successful.
     bool SaveJPG(const String& fileName, int quality) const;
+    /// Save in DDS format. Return true if successful.
+    bool SaveDDS(const String& fileName) const;
     /// Whether this texture is detected as a cubemap, only relevant for DDS.
     bool IsCubemap() const { return cubemap_; }
     /// Whether this texture has been detected as a volume, only relevant for DDS.
     bool IsArray() const { return array_; }
     /// Whether this texture is in sRGB, only relevant for DDS.
     bool IsSRGB() const { return sRGB_; }
+    /// Whether this texture has power of two dimensions
+    bool IsPOT() const;
+    /// Whether this texture has an alpha channel
+    bool HasAlphaChannel() const;
 
     /// Return a 2D pixel color.
     Color GetPixel(int x, int y) const;

+ 1 - 1
Source/Atomic/Resource/JSONFile.cpp

@@ -146,7 +146,7 @@ bool JSONFile::ParseJSON(const String& json, JSONValue& value, bool reportError)
     if (document.Parse<0>(json.CString()).HasParseError())
     {
         if (reportError)
-            LOGERROR("Could not parse JSON data from string");
+            LOGERRORF("Could not parse JSON data from string with error: %s", document.GetParseError());
 
         return false;
     }

+ 20 - 1
Source/Atomic/Resource/ResourceCache.cpp

@@ -595,7 +595,26 @@ Resource* ResourceCache::GetResource(StringHash type, const String& nameIn, bool
     }
 
     // Attempt to load the resource
-    SharedPtr<File> file = GetFile(name, sendEventOnFailure);
+    SharedPtr<File> file;
+
+    // #623 BEGIN TODO: For now try to get DDS version of textures from /DDS cache sub directory,
+    // ultimately should have per platform compressed versions saved in cache
+#ifdef ATOMIC_PLATFORM_DESKTOP
+    String ext = Atomic::GetExtension(name);
+    if (ext == ".jpg" || ext == ".png" || ext == ".tga")
+    {
+        String ddsName = "DDS/" + name + ".dds";
+        file = GetFile(ddsName, false);
+        if (file)
+            LOGDEBUG("Loaded cached DDS " + name + ".dds");
+    }
+    if (!file)
+        file = GetFile(name, sendEventOnFailure);
+#else
+    // #623 END TODO
+    file = GetFile(name, sendEventOnFailure);
+#endif
+
     if (!file)
         return 0;   // Error is already logged
 

+ 23 - 0
Source/Atomic/UI/UISelectItem.cpp

@@ -84,6 +84,29 @@ UISelectItemSource::~UISelectItemSource()
 
 }
 
+void UISelectItemSource::RemoveItemWithId(const String& id)
+{
+    tb::TBID test = TBID(id.CString());
+    for (List<SharedPtr<UISelectItem> >::Iterator itr = items_.Begin(); itr != items_.End(); itr++)
+    {
+        if ((*itr)->GetID() == test) {
+            items_.Erase(itr);
+            break;
+        }
+    }
+}
+
+void UISelectItemSource::RemoveItemWithStr(const String& str)
+{
+    for (List<SharedPtr<UISelectItem> >::Iterator itr = items_.Begin(); itr != items_.End(); itr++)
+    {
+        if ((*itr)->GetStr() == str) {
+            items_.Erase(itr);
+            break;
+        }
+    }
+}
+
 TBSelectItemSource *UISelectItemSource::GetTBItemSource()
 {
     // caller's responsibility to clean up

+ 5 - 0
Source/Atomic/UI/UISelectItem.h

@@ -44,6 +44,8 @@ public:
 
     void SetString(const String& str) { str_ = str; }
     void SetID(const String& id);
+    const String& GetStr() { return str_; }
+    tb::TBID GetID() { return id_; }
     void SetSkinImage(const String& skinImage);
     void SetSubSource(UISelectItemSource *subSource);
 
@@ -72,6 +74,9 @@ public:
     virtual ~UISelectItemSource();
 
     void AddItem(UISelectItem* item) { items_.Push(SharedPtr<UISelectItem>(item)); }
+    void RemoveItemWithId(const String& id);
+    void RemoveItemWithStr(const String& str);
+    int GetItemCount() { return items_.Size(); }
 
     void Clear() { items_.Clear(); }
 

+ 3 - 4
Source/AtomicEditor/Application/AEEditorCommon.cpp

@@ -54,7 +54,7 @@
 namespace Atomic
 {
     void jsapi_init_atomicnet(JSVM* vm);
-    void jsapi_init_webview(JSVM* vm);;
+    void jsapi_init_webview(JSVM* vm, const VariantMap& engineParameters);
 }
 
 using namespace ToolCore;
@@ -88,9 +88,8 @@ void AEEditorCommon::Start()
     jsapi_init_toolcore(vm_);
 
 #ifdef ATOMIC_WEBVIEW
-    // Initialize in Start so window already exists
-    context_->RegisterSubsystem(new WebBrowserHost(context_));
-    jsapi_init_webview(vm_);
+    // Initialize in Start so window already exists    
+    jsapi_init_webview(vm_, engineParameters_);
 #endif
 
 

+ 17 - 8
Source/AtomicEditor/Editors/SceneEditor3D/Gizmo3D.cpp

@@ -37,10 +37,10 @@
 namespace AtomicEditor
 {
 
-
-    Gizmo3D::Gizmo3D(Context* context) : Object(context),
-        dragging_(false),
-        cloning_(false)
+Gizmo3D::Gizmo3D(Context* context) : Object(context),
+    dragging_(false),
+    cloning_(false),
+    startClone_(false)
 {
     ResourceCache* cache = GetSubsystem<ResourceCache>();
 
@@ -185,10 +185,8 @@ void Gizmo3D::Use()
     Ray cameraRay = view3D_->GetCameraRay();
     float scale = gizmoNode_->GetScale().x_;
 
-    // Clones an object when it's dragged while holding down Shift
-    bool dragShift = input->GetMouseButtonDown(MOUSEB_LEFT) && input->GetKeyDown(KEY_SHIFT);
-
-    if (dragShift && !cloning_)
+    // Clones an object when it's dragged/rotated/scaled while holding down Shift
+    if (startClone_ && !cloning_)
     {
         cloning_ = true;
         selection_->Copy();
@@ -205,6 +203,7 @@ void Gizmo3D::Use()
             dragging_ = false;
         }
 
+        startClone_ = false;
         cloning_ = false;
 
         CalculateGizmoAxes();
@@ -429,6 +428,16 @@ void Gizmo3D::Drag()
 
     float scale = gizmoNode_->GetScale().x_;
 
+    Input* input = GetSubsystem<Input>();
+
+    //Activates cloning when any of these modes is used while holding shift
+    if (editMode_ == EDIT_MOVE ||
+        editMode_ == EDIT_ROTATE ||
+        editMode_ == EDIT_SCALE)
+    {
+        startClone_ = input->GetKeyDown(KEY_SHIFT);
+    }
+
     if (editMode_ == EDIT_MOVE)
     {
         Vector3 adjust(0, 0, 0);

+ 1 - 0
Source/AtomicEditor/Editors/SceneEditor3D/Gizmo3D.h

@@ -185,6 +185,7 @@ private:
 
     bool dragging_;
     bool cloning_;
+    bool startClone_;
 
     // snap settings
     float snapTranslationX_;

+ 3 - 0
Source/AtomicWebView/Internal/WebAppBrowser.cpp

@@ -35,6 +35,9 @@ WebAppBrowser::WebAppBrowser()
 
 void WebAppBrowser::OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr<CefCommandLine> command_line)
 {
+    command_line->AppendSwitch("--enable-media-stream");
+    command_line->AppendSwitch("--enable-usermedia-screen-capturing");
+
     CefApp::OnBeforeCommandLineProcessing(process_type, command_line);
 }
 

+ 17 - 8
Source/AtomicWebView/WebBrowserHost.cpp

@@ -97,6 +97,9 @@ private:
 
 GlobalPropertyMap WebBrowserHost::globalProperties_;
 WeakPtr<WebBrowserHost> WebBrowserHost::instance_;
+String WebBrowserHost::userAgent_;
+String WebBrowserHost::productVersion_;
+int WebBrowserHost::debugPort_ = 3335;
 
 WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 {
@@ -114,13 +117,10 @@ WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 #endif
 
 
-    // IMPORTANT: Cef::App contains virtual void OnBeforeCommandLineProcessing(), which should make it possible
-    // to setup args on Windows
+    // IMPORTANT: See flags being set in implementation of void WebAppBrowser::OnBeforeCommandLineProcessing
+    // these include "--enable-media-stream", "--enable-usermedia-screen-capturing"
 
-#ifdef ATOMIC_PLATFORM_OSX
-    const char* _argv[3] = { "", "--enable-media-stream", "--enable-usermedia-screen-capturing" };
-    CefMainArgs args(3, (char**) &_argv);
-#elif ATOMIC_PLATFORM_LINUX
+#ifdef ATOMIC_PLATFORM_LINUX
     static const char* _argv[3] = { "AtomicWebView", "--disable-setuid-sandbox", "--off-screen-rendering-enabled" };
     CefMainArgs args(3, (char**) &_argv);
 #else
@@ -129,9 +129,18 @@ WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 
     CefSettings settings;
     settings.windowless_rendering_enabled = true;
+
+    if (productVersion_.Length())
+    {
+        CefString(&settings.product_version).FromASCII(productVersion_.CString());
+    }
+
+    if (userAgent_.Length())
+    {
+        CefString(&settings.user_agent).FromASCII(userAgent_.CString());
+    }
     
-    // Enable remote debugging on port 3335
-    settings.remote_debugging_port = 3335;
+    settings.remote_debugging_port = debugPort_;
 
     d_ = new WebBrowserHostPrivate(this);
 

+ 24 - 0
Source/AtomicWebView/WebBrowserHost.h

@@ -50,6 +50,25 @@ public:
 
     static const GlobalPropertyMap& GetGlobalProperties() { return globalProperties_; }
 
+
+    // Configuration settings that must be set before creating WebBrowserHost subsystem
+
+    /// Set value that will be returned as the User-Agent HTTP header.
+    static void SetUserAgent(const String& userAgent) { userAgent_ = userAgent; }
+
+    /// Set value that will be inserted as the product portion of the default User-Agent string 
+    static void SetProductVersion(const String& productVersion) { productVersion_ = productVersion; }
+
+    /// Set to a value between 1024 and 65535 to enable remote debugging on the specified port
+    static void SetDebugPort(int debugPort) { debugPort_ = debugPort; }
+
+    /// Get User-Agent of the HTTP header. If empty the default User-Agent string will be used
+    static const String& GetUserAgent() { return userAgent_; }
+
+    /// Get value that will be inserted as the product portion of the default User-Agent string.  If empty the Chromium product version will be used      
+    static const String& GetProductVersion() { return productVersion_; }
+
+
 private:
 
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
@@ -62,6 +81,11 @@ private:
 
     static GlobalPropertyMap globalProperties_;
 
+    // configuration settings that must be set before WebBrowserHost subsystem is created
+    static String userAgent_;
+    static String productVersion_;
+    static int debugPort_;
+
 };
 
 }

+ 14 - 3
Source/AtomicWebView/WebClient.cpp

@@ -90,7 +90,7 @@ public:
     CefRefPtr<CefRenderHandler> GetRenderHandler() OVERRIDE
     {
 
-        if (webClient_->renderHandler_.Null())
+        if (webClient_.Null() || webClient_->renderHandler_.Null())
             return nullptr;
 
         return webClient_->renderHandler_->GetCEFRenderHandler();
@@ -487,6 +487,14 @@ public:
 
     IMPLEMENT_REFCOUNTING(WebClientPrivate);
 
+    void ClearReferences()
+    {
+        browser_ = nullptr;
+        webBrowserHost_ = nullptr;
+        webClient_ = nullptr;
+        browserSideRouter_ = nullptr;
+    }
+
 private:
 
     String initialLoadString_;
@@ -503,6 +511,7 @@ private:
 WebClient::WebClient(Context* context) : Object(context)
 {
     d_ = new WebClientPrivate(this);
+    d_->AddRef();
 
     SubscribeToEvent(E_WEBVIEWGLOBALPROPERTIESCHANGED, HANDLER(WebClient, HandleWebViewGlobalPropertiesChanged));
 }
@@ -520,10 +529,12 @@ WebClient::~WebClient()
         }
 
         d_->CloseBrowser(true);
+        d_->ClearReferences();
+        d_->Release();
     }
 
-    renderHandler_ = 0;
-    //d_->Release();
+    d_ = nullptr;
+    renderHandler_ = 0;    
 }
 
 void WebClient::SendMouseClickEvent(int x, int y, unsigned button, bool mouseUp, unsigned modifier, int clickCount) const

+ 15 - 1
Source/AtomicWebView/WebViewJS.cpp

@@ -20,6 +20,7 @@
 // THE SOFTWARE.
 //
 
+#include <Atomic/Engine/Engine.h>
 #include <AtomicJS/Javascript/JSVM.h>
 
 #include "WebBrowserHost.h"
@@ -29,10 +30,23 @@ namespace Atomic
 
 extern void jsb_package_webview_init(JSVM* vm);
 
-void jsapi_init_webview(JSVM* vm)
+void jsapi_init_webview(JSVM* vm, const VariantMap& engineParameters)
 {
     duk_context* ctx = vm->GetJSContext();
 
+    const String& userAgent = Engine::GetParameter(engineParameters, "WebViewUserAgent", Variant("")).GetString();
+    const String& productVersion = Engine::GetParameter(engineParameters, "WebViewProductVersion", Variant("")).GetString();
+    int debugPort = Engine::GetParameter(engineParameters, "WebViewDebugPort", Variant(3335)).GetInt();
+
+    if (userAgent.Length())
+        WebBrowserHost::SetUserAgent(userAgent);
+    if (productVersion.Length())
+        WebBrowserHost::SetProductVersion(productVersion);
+
+    WebBrowserHost::SetDebugPort(debugPort);
+
+    vm->GetContext()->RegisterSubsystem(new WebBrowserHost(vm->GetContext()));
+
     jsb_package_webview_init(vm);
 
     duk_get_global_string(ctx, "WebView");

+ 1 - 0
Source/ThirdParty/CMakeLists.txt

@@ -34,6 +34,7 @@ if (NOT IOS AND NOT ANDROID AND NOT EMSCRIPTEN)
     add_subdirectory(SQLite)
     add_subdirectory(Poco)
     add_subdirectory(nativefiledialog)
+    add_subdirectory(libsquish)
 endif ()
 
 if (LINUX OR APPLE AND NOT IOS)

+ 36 - 23
Source/ThirdParty/SDL/src/video/cocoa/SDL_cocoaevents.m

@@ -39,7 +39,7 @@
 @interface SDLApplication : NSApplication
 
 - (void)terminate:(id)sender;
-
+- (void)sendEvent:(NSEvent *)theEvent;
 @end
 
 @implementation SDLApplication
@@ -50,6 +50,41 @@
     SDL_SendQuit();
 }
 
+// ATOMIC: Fixes missing WebView keystrokes on OSX
+// (see https://bugzilla.libsdl.org/show_bug.cgi?id=3107)
+// Dispatch events here so that we can handle events caught by
+// nextEventMatchingMask in SDL, as well as events caught by other
+// processes (such as CEF) that are passed down to NSApp.
+- (void)sendEvent:(NSEvent *)theEvent
+{
+    SDL_VideoDevice *_this = SDL_GetVideoDevice();
+
+    switch ([theEvent type]) {
+        case NSLeftMouseDown:
+        case NSOtherMouseDown:
+        case NSRightMouseDown:
+        case NSLeftMouseUp:
+        case NSOtherMouseUp:
+        case NSRightMouseUp:
+        case NSLeftMouseDragged:
+        case NSRightMouseDragged:
+        case NSOtherMouseDragged: /* usually middle mouse dragged */
+        case NSMouseMoved:
+        case NSScrollWheel:
+            Cocoa_HandleMouseEvent(_this, theEvent);
+            break;
+        case NSKeyDown:
+        case NSKeyUp:
+        case NSFlagsChanged:
+            Cocoa_HandleKeyEvent(_this, theEvent);
+            break;
+        default:
+            break;
+    }
+
+    [super sendEvent:theEvent];
+}
+
 @end // SDLApplication
 
 /* setAppleMenu disappeared from the headers in 10.4 */
@@ -316,28 +351,6 @@ Cocoa_PumpEvents(_THIS)
             break;
         }
 
-        switch ([event type]) {
-        case NSLeftMouseDown:
-        case NSOtherMouseDown:
-        case NSRightMouseDown:
-        case NSLeftMouseUp:
-        case NSOtherMouseUp:
-        case NSRightMouseUp:
-        case NSLeftMouseDragged:
-        case NSRightMouseDragged:
-        case NSOtherMouseDragged: /* usually middle mouse dragged */
-        case NSMouseMoved:
-        case NSScrollWheel:
-            Cocoa_HandleMouseEvent(_this, event);
-            break;
-        case NSKeyDown:
-        case NSKeyUp:
-        case NSFlagsChanged:
-            Cocoa_HandleKeyEvent(_this, event);
-            break;
-        default:
-            break;
-        }
         /* Pass through to NSApp to make sure everything stays in sync */
         [NSApp sendEvent:event];
     }

+ 3 - 0
Source/ThirdParty/libsquish/CMakeLists.txt

@@ -0,0 +1,3 @@
+
+file (GLOB SOURCE_FILES *.cpp *.h)
+add_library(libsquish ${SOURCE_FILES})

+ 20 - 0
Source/ThirdParty/libsquish/LICENSE

@@ -0,0 +1,20 @@
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.

+ 35 - 0
Source/ThirdParty/libsquish/README

@@ -0,0 +1,35 @@
+LICENSE
+-------
+
+The squish library is distributed under the terms and conditions of the MIT
+license. This license is specified at the top of each source file and must be
+preserved in its entirety.
+
+BUILDING AND INSTALLING THE LIBRARY
+-----------------------------------
+
+If you are using Visual Studio 2003 or above under Windows then load the Visual
+Studio 2003 project in the vs7 folder. By default, the library is built using
+SSE2 optimisations. To change this either change or remove the SQUISH_USE_SSE=2
+from the preprocessor symbols.
+
+If you are using a Mac then load the Xcode 2.2 project in the distribution. By
+default, the library is built using Altivec optimisations. To change this
+either change or remove SQUISH_USE_ALTIVEC=1 from the preprocessor symbols. I
+guess I'll have to think about changing this for the new Intel Macs that are
+rolling out...
+
+If you are using unix then first edit the config file in the base directory of
+the distribution, enabling Altivec or SSE with the USE_ALTIVEC or USE_SSE
+variables, and editing the optimisation flags passed to the C++ compiler if
+necessary. Then make can be used to build the library, and make install (from
+the superuser account) can be used to install (into /usr/local by default).
+
+REPORTING BUGS OR FEATURE REQUESTS
+----------------------------------
+
+Feedback can be sent to Simon Brown (the developer) at [email protected]
+
+New releases are announced on the squish library homepage at
+http://sjbrown.co.uk/?code=squish
+

+ 350 - 0
Source/ThirdParty/libsquish/alpha.cpp

@@ -0,0 +1,350 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#include "alpha.h"
+
+#include <climits>
+#include <algorithm>
+
+namespace squish {
+
+static int FloatToInt( float a, int limit )
+{
+	// use ANSI round-to-zero behaviour to get round-to-nearest
+	int i = ( int )( a + 0.5f );
+
+	// clamp to the limit
+	if( i < 0 )
+		i = 0;
+	else if( i > limit )
+		i = limit; 
+
+	// done
+	return i;
+}
+
+void CompressAlphaDxt3( u8 const* rgba, int mask, void* block )
+{
+	u8* bytes = reinterpret_cast< u8* >( block );
+	
+	// quantise and pack the alpha values pairwise
+	for( int i = 0; i < 8; ++i )
+	{
+		// quantise down to 4 bits
+		float alpha1 = ( float )rgba[8*i + 3] * ( 15.0f/255.0f );
+		float alpha2 = ( float )rgba[8*i + 7] * ( 15.0f/255.0f );
+		int quant1 = FloatToInt( alpha1, 15 );
+		int quant2 = FloatToInt( alpha2, 15 );
+		
+		// set alpha to zero where masked
+		int bit1 = 1 << ( 2*i );
+		int bit2 = 1 << ( 2*i + 1 );
+		if( ( mask & bit1 ) == 0 )
+			quant1 = 0;
+		if( ( mask & bit2 ) == 0 )
+			quant2 = 0;
+
+		// pack into the byte
+		bytes[i] = ( u8 )( quant1 | ( quant2 << 4 ) );
+	}
+}
+
+void DecompressAlphaDxt3( u8* rgba, void const* block )
+{
+	u8 const* bytes = reinterpret_cast< u8 const* >( block );
+	
+	// unpack the alpha values pairwise
+	for( int i = 0; i < 8; ++i )
+	{
+		// quantise down to 4 bits
+		u8 quant = bytes[i];
+		
+		// unpack the values
+		u8 lo = quant & 0x0f;
+		u8 hi = quant & 0xf0;
+
+		// convert back up to bytes
+		rgba[8*i + 3] = lo | ( lo << 4 );
+		rgba[8*i + 7] = hi | ( hi >> 4 );
+	}
+}
+
+static void FixRange( int& min, int& max, int steps )
+{
+	if( max - min < steps )
+		max = std::min( min + steps, 255 );
+	if( max - min < steps )
+		min = std::max( 0, max - steps );
+}
+
+static int FitCodes( u8 const* rgba, int mask, u8 const* codes, u8* indices )
+{
+	// fit each alpha value to the codebook
+	int err = 0;
+	for( int i = 0; i < 16; ++i )
+	{
+		// check this pixel is valid
+		int bit = 1 << i;
+		if( ( mask & bit ) == 0 )
+		{
+			// use the first code
+			indices[i] = 0;
+			continue;
+		}
+		
+		// find the least error and corresponding index
+		int value = rgba[4*i + 3];
+		int least = INT_MAX;
+		int index = 0;
+		for( int j = 0; j < 8; ++j )
+		{
+			// get the squared error from this code
+			int dist = ( int )value - ( int )codes[j];
+			dist *= dist;
+			
+			// compare with the best so far
+			if( dist < least )
+			{
+				least = dist;
+				index = j;
+			}
+		}
+		
+		// save this index and accumulate the error
+		indices[i] = ( u8 )index;
+		err += least;
+	}
+	
+	// return the total error
+	return err;
+}
+
+static void WriteAlphaBlock( int alpha0, int alpha1, u8 const* indices, void* block )
+{
+	u8* bytes = reinterpret_cast< u8* >( block );
+	
+	// write the first two bytes
+	bytes[0] = ( u8 )alpha0;
+	bytes[1] = ( u8 )alpha1;
+	
+	// pack the indices with 3 bits each
+	u8* dest = bytes + 2;
+	u8 const* src = indices;
+	for( int i = 0; i < 2; ++i )
+	{
+		// pack 8 3-bit values
+		int value = 0;
+		for( int j = 0; j < 8; ++j )
+		{
+			int index = *src++;
+			value |= ( index << 3*j );
+		}
+			
+		// store in 3 bytes
+		for( int j = 0; j < 3; ++j )
+		{
+			int byte = ( value >> 8*j ) & 0xff;
+			*dest++ = ( u8 )byte;
+		}
+	}
+}
+
+static void WriteAlphaBlock5( int alpha0, int alpha1, u8 const* indices, void* block )
+{
+	// check the relative values of the endpoints
+	if( alpha0 > alpha1 )
+	{
+		// swap the indices
+		u8 swapped[16];
+		for( int i = 0; i < 16; ++i )
+		{
+			u8 index = indices[i];
+			if( index == 0 )
+				swapped[i] = 1;
+			else if( index == 1 )
+				swapped[i] = 0;
+			else if( index <= 5 )
+				swapped[i] = 7 - index;
+			else 
+				swapped[i] = index;
+		}
+		
+		// write the block
+		WriteAlphaBlock( alpha1, alpha0, swapped, block );
+	}
+	else
+	{
+		// write the block
+		WriteAlphaBlock( alpha0, alpha1, indices, block );
+	}	
+}
+
+static void WriteAlphaBlock7( int alpha0, int alpha1, u8 const* indices, void* block )
+{
+	// check the relative values of the endpoints
+	if( alpha0 < alpha1 )
+	{
+		// swap the indices
+		u8 swapped[16];
+		for( int i = 0; i < 16; ++i )
+		{
+			u8 index = indices[i];
+			if( index == 0 )
+				swapped[i] = 1;
+			else if( index == 1 )
+				swapped[i] = 0;
+			else
+				swapped[i] = 9 - index;
+		}
+		
+		// write the block
+		WriteAlphaBlock( alpha1, alpha0, swapped, block );
+	}
+	else
+	{
+		// write the block
+		WriteAlphaBlock( alpha0, alpha1, indices, block );
+	}	
+}
+
+void CompressAlphaDxt5( u8 const* rgba, int mask, void* block )
+{
+	// get the range for 5-alpha and 7-alpha interpolation
+	int min5 = 255;
+	int max5 = 0;
+	int min7 = 255;
+	int max7 = 0;
+	for( int i = 0; i < 16; ++i )
+	{
+		// check this pixel is valid
+		int bit = 1 << i;
+		if( ( mask & bit ) == 0 )
+			continue;
+
+		// incorporate into the min/max
+		int value = rgba[4*i + 3];
+		if( value < min7 )
+			min7 = value;
+		if( value > max7 )
+			max7 = value;
+		if( value != 0 && value < min5 )
+			min5 = value;
+		if( value != 255 && value > max5 )
+			max5 = value;
+	}
+	
+	// handle the case that no valid range was found
+	if( min5 > max5 )
+		min5 = max5;
+	if( min7 > max7 )
+		min7 = max7;
+		
+	// fix the range to be the minimum in each case
+	FixRange( min5, max5, 5 );
+	FixRange( min7, max7, 7 );
+	
+	// set up the 5-alpha code book
+	u8 codes5[8];
+	codes5[0] = ( u8 )min5;
+	codes5[1] = ( u8 )max5;
+	for( int i = 1; i < 5; ++i )
+		codes5[1 + i] = ( u8 )( ( ( 5 - i )*min5 + i*max5 )/5 );
+	codes5[6] = 0;
+	codes5[7] = 255;
+	
+	// set up the 7-alpha code book
+	u8 codes7[8];
+	codes7[0] = ( u8 )min7;
+	codes7[1] = ( u8 )max7;
+	for( int i = 1; i < 7; ++i )
+		codes7[1 + i] = ( u8 )( ( ( 7 - i )*min7 + i*max7 )/7 );
+		
+	// fit the data to both code books
+	u8 indices5[16];
+	u8 indices7[16];
+	int err5 = FitCodes( rgba, mask, codes5, indices5 );
+	int err7 = FitCodes( rgba, mask, codes7, indices7 );
+	
+	// save the block with least error
+	if( err5 <= err7 )
+		WriteAlphaBlock5( min5, max5, indices5, block );
+	else
+		WriteAlphaBlock7( min7, max7, indices7, block );
+}
+
+void DecompressAlphaDxt5( u8* rgba, void const* block )
+{
+	// get the two alpha values
+	u8 const* bytes = reinterpret_cast< u8 const* >( block );
+	int alpha0 = bytes[0];
+	int alpha1 = bytes[1];
+	
+	// compare the values to build the codebook
+	u8 codes[8];
+	codes[0] = ( u8 )alpha0;
+	codes[1] = ( u8 )alpha1;
+	if( alpha0 <= alpha1 )
+	{
+		// use 5-alpha codebook
+		for( int i = 1; i < 5; ++i )
+			codes[1 + i] = ( u8 )( ( ( 5 - i )*alpha0 + i*alpha1 )/5 );
+		codes[6] = 0;
+		codes[7] = 255;
+	}
+	else
+	{
+		// use 7-alpha codebook
+		for( int i = 1; i < 7; ++i )
+			codes[1 + i] = ( u8 )( ( ( 7 - i )*alpha0 + i*alpha1 )/7 );
+	}
+	
+	// decode the indices
+	u8 indices[16];
+	u8 const* src = bytes + 2;
+	u8* dest = indices;
+	for( int i = 0; i < 2; ++i )
+	{
+		// grab 3 bytes
+		int value = 0;
+		for( int j = 0; j < 3; ++j )
+		{
+			int byte = *src++;
+			value |= ( byte << 8*j );
+		}
+		
+		// unpack 8 3-bit values from it
+		for( int j = 0; j < 8; ++j )
+		{
+			int index = ( value >> 3*j ) & 0x7;
+			*dest++ = ( u8 )index;
+		}
+	}
+	
+	// write out the indexed codebook values
+	for( int i = 0; i < 16; ++i )
+		rgba[4*i + 3] = codes[indices[i]];
+}
+
+} // namespace squish

+ 41 - 0
Source/ThirdParty/libsquish/alpha.h

@@ -0,0 +1,41 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_ALPHA_H
+#define SQUISH_ALPHA_H
+
+#include "squish.h"
+
+namespace squish {
+
+void CompressAlphaDxt3( u8 const* rgba, int mask, void* block );
+void CompressAlphaDxt5( u8 const* rgba, int mask, void* block );
+
+void DecompressAlphaDxt3( u8* rgba, void const* block );
+void DecompressAlphaDxt5( u8* rgba, void const* block );
+
+} // namespace squish
+
+#endif // ndef SQUISH_ALPHA_H

+ 392 - 0
Source/ThirdParty/libsquish/clusterfit.cpp

@@ -0,0 +1,392 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+	Copyright (c) 2007 Ignacio Castano                   [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#include "clusterfit.h"
+#include "colourset.h"
+#include "colourblock.h"
+#include <cfloat>
+
+namespace squish {
+
+ClusterFit::ClusterFit( ColourSet const* colours, int flags, float* metric ) 
+  : ColourFit( colours, flags )
+{
+	// set the iteration count
+	m_iterationCount = ( m_flags & kColourIterativeClusterFit ) ? kMaxIterations : 1;
+
+	// initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)
+	if( metric )
+		m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f );
+	else
+		m_metric = VEC4_CONST( 1.0f );	
+
+	// initialise the best error
+	m_besterror = VEC4_CONST( FLT_MAX );
+
+	// cache some values
+	int const count = m_colours->GetCount();
+	Vec3 const* values = m_colours->GetPoints();
+
+	// get the covariance matrix
+	Sym3x3 covariance = ComputeWeightedCovariance( count, values, m_colours->GetWeights() );
+	
+	// compute the principle component
+	m_principle = ComputePrincipleComponent( covariance );
+}
+
+bool ClusterFit::ConstructOrdering( Vec3 const& axis, int iteration )
+{
+	// cache some values
+	int const count = m_colours->GetCount();
+	Vec3 const* values = m_colours->GetPoints();
+
+	// build the list of dot products
+	float dps[16];
+	u8* order = ( u8* )m_order + 16*iteration;
+	for( int i = 0; i < count; ++i )
+	{
+		dps[i] = Dot( values[i], axis );
+		order[i] = ( u8 )i;
+	}
+		
+	// stable sort using them
+	for( int i = 0; i < count; ++i )
+	{
+		for( int j = i; j > 0 && dps[j] < dps[j - 1]; --j )
+		{
+			std::swap( dps[j], dps[j - 1] );
+			std::swap( order[j], order[j - 1] );
+		}
+	}
+	
+	// check this ordering is unique
+	for( int it = 0; it < iteration; ++it )
+	{
+		u8 const* prev = ( u8* )m_order + 16*it;
+		bool same = true;
+		for( int i = 0; i < count; ++i )
+		{
+			if( order[i] != prev[i] )
+			{
+				same = false;
+				break;
+			}
+		}
+		if( same )
+			return false;
+	}
+	
+	// copy the ordering and weight all the points
+	Vec3 const* unweighted = m_colours->GetPoints();
+	float const* weights = m_colours->GetWeights();
+	m_xsum_wsum = VEC4_CONST( 0.0f );
+	for( int i = 0; i < count; ++i )
+	{
+		int j = order[i];
+		Vec4 p( unweighted[j].X(), unweighted[j].Y(), unweighted[j].Z(), 1.0f );
+		Vec4 w( weights[j] );
+		Vec4 x = p*w;
+		m_points_weights[i] = x;
+		m_xsum_wsum += x;
+	}
+	return true;
+}
+
+void ClusterFit::Compress3( void* block )
+{
+	// declare variables
+	int const count = m_colours->GetCount();
+	Vec4 const two = VEC4_CONST( 2.0 );
+	Vec4 const one = VEC4_CONST( 1.0f );
+	Vec4 const half_half2( 0.5f, 0.5f, 0.5f, 0.25f );
+	Vec4 const zero = VEC4_CONST( 0.0f );
+	Vec4 const half = VEC4_CONST( 0.5f );
+	Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f );
+	Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f );
+
+	// prepare an ordering using the principle axis
+	ConstructOrdering( m_principle, 0 );
+	
+	// check all possible clusters and iterate on the total order
+	Vec4 beststart = VEC4_CONST( 0.0f );
+	Vec4 bestend = VEC4_CONST( 0.0f );
+	Vec4 besterror = m_besterror;
+	u8 bestindices[16];
+	int bestiteration = 0;
+	int besti = 0, bestj = 0;
+	
+	// loop over iterations (we avoid the case that all points in first or last cluster)
+	for( int iterationIndex = 0;; )
+	{
+		// first cluster [0,i) is at the start
+		Vec4 part0 = VEC4_CONST( 0.0f );
+		for( int i = 0; i < count; ++i )
+		{
+			// second cluster [i,j) is half along
+			Vec4 part1 = ( i == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f );
+			int jmin = ( i == 0 ) ? 1 : i;
+			for( int j = jmin;; )
+			{
+				// last cluster [j,count) is at the end
+				Vec4 part2 = m_xsum_wsum - part1 - part0;
+				
+				// compute least squares terms directly
+				Vec4 alphax_sum = MultiplyAdd( part1, half_half2, part0 );
+				Vec4 alpha2_sum = alphax_sum.SplatW();
+
+				Vec4 betax_sum = MultiplyAdd( part1, half_half2, part2 );
+				Vec4 beta2_sum = betax_sum.SplatW();
+
+				Vec4 alphabeta_sum = ( part1*half_half2 ).SplatW();
+
+				// compute the least-squares optimal points
+				Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) );
+				Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor;
+				Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor;
+
+				// clamp to the grid
+				a = Min( one, Max( zero, a ) );
+				b = Min( one, Max( zero, b ) );
+				a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp;
+				b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp;
+				
+				// compute the error (we skip the constant xxsum)
+				Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum );
+				Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum );
+				Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 );
+				Vec4 e4 = MultiplyAdd( two, e3, e1 );
+
+				// apply the metric to the error term
+				Vec4 e5 = e4*m_metric;
+				Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ();
+				
+				// keep the solution if it wins
+				if( CompareAnyLessThan( error, besterror ) )
+				{
+					beststart = a;
+					bestend = b;
+					besti = i;
+					bestj = j;
+					besterror = error;
+					bestiteration = iterationIndex;
+				}
+
+				// advance
+				if( j == count )
+					break;
+				part1 += m_points_weights[j];
+				++j;
+			}
+
+			// advance
+			part0 += m_points_weights[i];
+		}
+		
+		// stop if we didn't improve in this iteration
+		if( bestiteration != iterationIndex )
+			break;
+			
+		// advance if possible
+		++iterationIndex;
+		if( iterationIndex == m_iterationCount )
+			break;
+			
+		// stop if a new iteration is an ordering that has already been tried
+		Vec3 axis = ( bestend - beststart ).GetVec3();
+		if( !ConstructOrdering( axis, iterationIndex ) )
+			break;
+	}
+		
+	// save the block if necessary
+	if( CompareAnyLessThan( besterror, m_besterror ) )
+	{
+		// remap the indices
+		u8 const* order = ( u8* )m_order + 16*bestiteration;
+
+		u8 unordered[16];
+		for( int m = 0; m < besti; ++m )
+			unordered[order[m]] = 0;
+		for( int m = besti; m < bestj; ++m )
+			unordered[order[m]] = 2;
+		for( int m = bestj; m < count; ++m )
+			unordered[order[m]] = 1;
+
+		m_colours->RemapIndices( unordered, bestindices );
+		
+		// save the block
+		WriteColourBlock3( beststart.GetVec3(), bestend.GetVec3(), bestindices, block );
+
+		// save the error
+		m_besterror = besterror;
+	}
+}
+
+void ClusterFit::Compress4( void* block )
+{
+	// declare variables
+	int const count = m_colours->GetCount();
+	Vec4 const two = VEC4_CONST( 2.0f );
+	Vec4 const one = VEC4_CONST( 1.0f );
+	Vec4 const onethird_onethird2( 1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f, 1.0f/9.0f );
+	Vec4 const twothirds_twothirds2( 2.0f/3.0f, 2.0f/3.0f, 2.0f/3.0f, 4.0f/9.0f );
+	Vec4 const twonineths = VEC4_CONST( 2.0f/9.0f );
+	Vec4 const zero = VEC4_CONST( 0.0f );
+	Vec4 const half = VEC4_CONST( 0.5f );
+	Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f );
+	Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f );
+
+	// prepare an ordering using the principle axis
+	ConstructOrdering( m_principle, 0 );
+	
+	// check all possible clusters and iterate on the total order
+	Vec4 beststart = VEC4_CONST( 0.0f );
+	Vec4 bestend = VEC4_CONST( 0.0f );
+	Vec4 besterror = m_besterror;
+	u8 bestindices[16];
+	int bestiteration = 0;
+	int besti = 0, bestj = 0, bestk = 0;
+	
+	// loop over iterations (we avoid the case that all points in first or last cluster)
+	for( int iterationIndex = 0;; )
+	{
+		// first cluster [0,i) is at the start
+		Vec4 part0 = VEC4_CONST( 0.0f );
+		for( int i = 0; i < count; ++i )
+		{
+			// second cluster [i,j) is one third along
+			Vec4 part1 = VEC4_CONST( 0.0f );
+			for( int j = i;; )
+			{
+				// third cluster [j,k) is two thirds along
+				Vec4 part2 = ( j == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f );
+				int kmin = ( j == 0 ) ? 1 : j;
+				for( int k = kmin;; )
+				{
+					// last cluster [k,count) is at the end
+					Vec4 part3 = m_xsum_wsum - part2 - part1 - part0;
+
+					// compute least squares terms directly
+					Vec4 const alphax_sum = MultiplyAdd( part2, onethird_onethird2, MultiplyAdd( part1, twothirds_twothirds2, part0 ) );
+					Vec4 const alpha2_sum = alphax_sum.SplatW();
+					
+					Vec4 const betax_sum = MultiplyAdd( part1, onethird_onethird2, MultiplyAdd( part2, twothirds_twothirds2, part3 ) );
+					Vec4 const beta2_sum = betax_sum.SplatW();
+					
+					Vec4 const alphabeta_sum = twonineths*( part1 + part2 ).SplatW();
+
+					// compute the least-squares optimal points
+					Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) );
+					Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor;
+					Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor;
+
+					// clamp to the grid
+					a = Min( one, Max( zero, a ) );
+					b = Min( one, Max( zero, b ) );
+					a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp;
+					b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp;
+					
+					// compute the error (we skip the constant xxsum)
+					Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum );
+					Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum );
+					Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 );
+					Vec4 e4 = MultiplyAdd( two, e3, e1 );
+
+					// apply the metric to the error term
+					Vec4 e5 = e4*m_metric;
+					Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ();
+
+					// keep the solution if it wins
+					if( CompareAnyLessThan( error, besterror ) )
+					{
+						beststart = a;
+						bestend = b;
+						besterror = error;
+						besti = i;
+						bestj = j;
+						bestk = k;
+						bestiteration = iterationIndex;
+					}
+
+					// advance
+					if( k == count )
+						break;
+					part2 += m_points_weights[k];
+					++k;
+				}
+
+				// advance
+				if( j == count )
+					break;
+				part1 += m_points_weights[j];
+				++j;
+			}
+
+			// advance
+			part0 += m_points_weights[i];
+		}
+		
+		// stop if we didn't improve in this iteration
+		if( bestiteration != iterationIndex )
+			break;
+			
+		// advance if possible
+		++iterationIndex;
+		if( iterationIndex == m_iterationCount )
+			break;
+			
+		// stop if a new iteration is an ordering that has already been tried
+		Vec3 axis = ( bestend - beststart ).GetVec3();
+		if( !ConstructOrdering( axis, iterationIndex ) )
+			break;
+	}
+
+	// save the block if necessary
+	if( CompareAnyLessThan( besterror, m_besterror ) )
+	{
+		// remap the indices
+		u8 const* order = ( u8* )m_order + 16*bestiteration;
+
+		u8 unordered[16];
+		for( int m = 0; m < besti; ++m )
+			unordered[order[m]] = 0;
+		for( int m = besti; m < bestj; ++m )
+			unordered[order[m]] = 2;
+		for( int m = bestj; m < bestk; ++m )
+			unordered[order[m]] = 3;
+		for( int m = bestk; m < count; ++m )
+			unordered[order[m]] = 1;
+
+		m_colours->RemapIndices( unordered, bestindices );
+		
+		// save the block
+		WriteColourBlock4( beststart.GetVec3(), bestend.GetVec3(), bestindices, block );
+
+		// save the error
+		m_besterror = besterror;
+	}
+}
+
+} // namespace squish

+ 61 - 0
Source/ThirdParty/libsquish/clusterfit.h

@@ -0,0 +1,61 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+	Copyright (c) 2007 Ignacio Castano                   [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_CLUSTERFIT_H
+#define SQUISH_CLUSTERFIT_H
+
+#include "squish.h"
+#include "maths.h"
+#include "simd.h"
+#include "colourfit.h"
+
+namespace squish {
+
+class ClusterFit : public ColourFit
+{
+public:
+	ClusterFit( ColourSet const* colours, int flags, float* metric );
+	
+private:
+	bool ConstructOrdering( Vec3 const& axis, int iteration );
+
+	virtual void Compress3( void* block );
+	virtual void Compress4( void* block );
+
+	enum { kMaxIterations = 8 };
+
+	int m_iterationCount;
+	Vec3 m_principle;
+	u8 m_order[16*kMaxIterations];
+	Vec4 m_points_weights[16];
+	Vec4 m_xsum_wsum;
+	Vec4 m_metric;
+	Vec4 m_besterror;
+};
+
+} // namespace squish
+
+#endif // ndef SQUISH_CLUSTERFIT_H

+ 214 - 0
Source/ThirdParty/libsquish/colourblock.cpp

@@ -0,0 +1,214 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#include "colourblock.h"
+
+namespace squish {
+
+static int FloatToInt( float a, int limit )
+{
+	// use ANSI round-to-zero behaviour to get round-to-nearest
+	int i = ( int )( a + 0.5f );
+
+	// clamp to the limit
+	if( i < 0 )
+		i = 0;
+	else if( i > limit )
+		i = limit; 
+
+	// done
+	return i;
+}
+
+static int FloatTo565( Vec3::Arg colour )
+{
+	// get the components in the correct range
+	int r = FloatToInt( 31.0f*colour.X(), 31 );
+	int g = FloatToInt( 63.0f*colour.Y(), 63 );
+	int b = FloatToInt( 31.0f*colour.Z(), 31 );
+	
+	// pack into a single value
+	return ( r << 11 ) | ( g << 5 ) | b;
+}
+
+static void WriteColourBlock( int a, int b, u8* indices, void* block )
+{
+	// get the block as bytes
+	u8* bytes = ( u8* )block;
+
+	// write the endpoints
+	bytes[0] = ( u8 )( a & 0xff );
+	bytes[1] = ( u8 )( a >> 8 );
+	bytes[2] = ( u8 )( b & 0xff );
+	bytes[3] = ( u8 )( b >> 8 );
+	
+	// write the indices
+	for( int i = 0; i < 4; ++i )
+	{
+		u8 const* ind = indices + 4*i;
+		bytes[4 + i] = ind[0] | ( ind[1] << 2 ) | ( ind[2] << 4 ) | ( ind[3] << 6 );
+	}
+}
+
+void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block )
+{
+	// get the packed values
+	int a = FloatTo565( start );
+	int b = FloatTo565( end );
+
+	// remap the indices
+	u8 remapped[16];
+	if( a <= b )
+	{
+		// use the indices directly
+		for( int i = 0; i < 16; ++i )
+			remapped[i] = indices[i];
+	}
+	else
+	{
+		// swap a and b
+		std::swap( a, b );
+		for( int i = 0; i < 16; ++i )
+		{
+			if( indices[i] == 0 )
+				remapped[i] = 1;
+			else if( indices[i] == 1 )
+				remapped[i] = 0;
+			else
+				remapped[i] = indices[i];
+		}
+	}
+	
+	// write the block
+	WriteColourBlock( a, b, remapped, block );
+}
+
+void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block )
+{
+	// get the packed values
+	int a = FloatTo565( start );
+	int b = FloatTo565( end );
+
+	// remap the indices
+	u8 remapped[16];
+	if( a < b )
+	{
+		// swap a and b
+		std::swap( a, b );
+		for( int i = 0; i < 16; ++i )
+			remapped[i] = ( indices[i] ^ 0x1 ) & 0x3;
+	}
+	else if( a == b )
+	{
+		// use index 0
+		for( int i = 0; i < 16; ++i )
+			remapped[i] = 0;
+	}
+	else
+	{
+		// use the indices directly
+		for( int i = 0; i < 16; ++i )
+			remapped[i] = indices[i];
+	}
+	
+	// write the block
+	WriteColourBlock( a, b, remapped, block );
+}
+
+static int Unpack565( u8 const* packed, u8* colour )
+{
+	// build the packed value
+	int value = ( int )packed[0] | ( ( int )packed[1] << 8 );
+	
+	// get the components in the stored range
+	u8 red = ( u8 )( ( value >> 11 ) & 0x1f );
+	u8 green = ( u8 )( ( value >> 5 ) & 0x3f );
+	u8 blue = ( u8 )( value & 0x1f );
+
+	// scale up to 8 bits
+	colour[0] = ( red << 3 ) | ( red >> 2 );
+	colour[1] = ( green << 2 ) | ( green >> 4 );
+	colour[2] = ( blue << 3 ) | ( blue >> 2 );
+	colour[3] = 255;
+	
+	// return the value
+	return value;
+}
+
+void DecompressColour( u8* rgba, void const* block, bool isDxt1 )
+{
+	// get the block bytes
+	u8 const* bytes = reinterpret_cast< u8 const* >( block );
+	
+	// unpack the endpoints
+	u8 codes[16];
+	int a = Unpack565( bytes, codes );
+	int b = Unpack565( bytes + 2, codes + 4 );
+	
+	// generate the midpoints
+	for( int i = 0; i < 3; ++i )
+	{
+		int c = codes[i];
+		int d = codes[4 + i];
+
+		if( isDxt1 && a <= b )
+		{
+			codes[8 + i] = ( u8 )( ( c + d )/2 );
+			codes[12 + i] = 0;
+		}
+		else
+		{
+			codes[8 + i] = ( u8 )( ( 2*c + d )/3 );
+			codes[12 + i] = ( u8 )( ( c + 2*d )/3 );
+		}
+	}
+	
+	// fill in alpha for the intermediate values
+	codes[8 + 3] = 255;
+	codes[12 + 3] = ( isDxt1 && a <= b ) ? 0 : 255;
+	
+	// unpack the indices
+	u8 indices[16];
+	for( int i = 0; i < 4; ++i )
+	{
+		u8* ind = indices + 4*i;
+		u8 packed = bytes[4 + i];
+		
+		ind[0] = packed & 0x3;
+		ind[1] = ( packed >> 2 ) & 0x3;
+		ind[2] = ( packed >> 4 ) & 0x3;
+		ind[3] = ( packed >> 6 ) & 0x3;
+	}
+
+	// store out the colours
+	for( int i = 0; i < 16; ++i )
+	{
+		u8 offset = 4*indices[i];
+		for( int j = 0; j < 4; ++j )
+			rgba[4*i + j] = codes[offset + j];
+	}
+}
+
+} // namespace squish

+ 41 - 0
Source/ThirdParty/libsquish/colourblock.h

@@ -0,0 +1,41 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_COLOURBLOCK_H
+#define SQUISH_COLOURBLOCK_H
+
+#include "squish.h"
+#include "maths.h"
+
+namespace squish {
+
+void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block );
+void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block );
+
+void DecompressColour( u8* rgba, void const* block, bool isDxt1 );
+
+} // namespace squish
+
+#endif // ndef SQUISH_COLOURBLOCK_H

+ 54 - 0
Source/ThirdParty/libsquish/colourfit.cpp

@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#include "colourfit.h"
+#include "colourset.h"
+
+namespace squish {
+
+ColourFit::ColourFit( ColourSet const* colours, int flags ) 
+  : m_colours( colours ), 
+	m_flags( flags )
+{
+}
+
+ColourFit::~ColourFit()
+{
+}
+
+void ColourFit::Compress( void* block )
+{
+	bool isDxt1 = ( ( m_flags & kDxt1 ) != 0 );
+	if( isDxt1 )
+	{
+		Compress3( block );
+		if( !m_colours->IsTransparent() )
+			Compress4( block );
+	}
+	else
+		Compress4( block );
+}
+
+} // namespace squish

+ 56 - 0
Source/ThirdParty/libsquish/colourfit.h

@@ -0,0 +1,56 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_COLOURFIT_H
+#define SQUISH_COLOURFIT_H
+
+#include "squish.h"
+#include "maths.h"
+
+#include <climits>
+
+namespace squish {
+
+class ColourSet;
+
+class ColourFit
+{
+public:
+	ColourFit( ColourSet const* colours, int flags );
+	virtual ~ColourFit();
+
+	void Compress( void* block );
+
+protected:
+	virtual void Compress3( void* block ) = 0;
+	virtual void Compress4( void* block ) = 0;
+
+	ColourSet const* m_colours;
+	int m_flags;
+};
+
+} // namespace squish
+
+#endif // ndef SQUISH_COLOURFIT_H

+ 121 - 0
Source/ThirdParty/libsquish/colourset.cpp

@@ -0,0 +1,121 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#include "colourset.h"
+
+namespace squish {
+
+ColourSet::ColourSet( u8 const* rgba, int mask, int flags )
+  : m_count( 0 ), 
+	m_transparent( false )
+{
+	// check the compression mode for dxt1
+	bool isDxt1 = ( ( flags & kDxt1 ) != 0 );
+	bool weightByAlpha = ( ( flags & kWeightColourByAlpha ) != 0 );
+
+	// create the minimal set
+	for( int i = 0; i < 16; ++i )
+	{
+		// check this pixel is enabled
+		int bit = 1 << i;
+		if( ( mask & bit ) == 0 )
+		{
+			m_remap[i] = -1;
+			continue;
+		}
+	
+		// check for transparent pixels when using dxt1
+		if( isDxt1 && rgba[4*i + 3] < 128 )
+		{
+			m_remap[i] = -1;
+			m_transparent = true;
+			continue;
+		}
+
+		// loop over previous points for a match
+		for( int j = 0;; ++j )
+		{
+			// allocate a new point
+			if( j == i )
+			{
+				// normalise coordinates to [0,1]
+				float x = ( float )rgba[4*i] / 255.0f;
+				float y = ( float )rgba[4*i + 1] / 255.0f;
+				float z = ( float )rgba[4*i + 2] / 255.0f;
+				
+				// ensure there is always non-zero weight even for zero alpha
+				float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f;
+
+				// add the point
+				m_points[m_count] = Vec3( x, y, z );
+				m_weights[m_count] = ( weightByAlpha ? w : 1.0f );
+				m_remap[i] = m_count;
+				
+				// advance
+				++m_count;
+				break;
+			}
+		
+			// check for a match
+			int oldbit = 1 << j;
+			bool match = ( ( mask & oldbit ) != 0 )
+				&& ( rgba[4*i] == rgba[4*j] )
+				&& ( rgba[4*i + 1] == rgba[4*j + 1] )
+				&& ( rgba[4*i + 2] == rgba[4*j + 2] )
+				&& ( rgba[4*j + 3] >= 128 || !isDxt1 );
+			if( match )
+			{
+				// get the index of the match
+				int index = m_remap[j];
+				
+				// ensure there is always non-zero weight even for zero alpha
+				float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f;
+
+				// map to this point and increase the weight
+				m_weights[index] += ( weightByAlpha ? w : 1.0f );
+				m_remap[i] = index;
+				break;
+			}
+		}
+	}
+
+	// square root the weights
+	for( int i = 0; i < m_count; ++i )
+		m_weights[i] = std::sqrt( m_weights[i] );
+}
+
+void ColourSet::RemapIndices( u8 const* source, u8* target ) const
+{
+	for( int i = 0; i < 16; ++i )
+	{
+		int j = m_remap[i];
+		if( j == -1 )
+			target[i] = 3;
+		else
+			target[i] = source[j];
+	}
+}
+
+} // namespace squish

+ 58 - 0
Source/ThirdParty/libsquish/colourset.h

@@ -0,0 +1,58 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_COLOURSET_H
+#define SQUISH_COLOURSET_H
+
+#include "squish.h"
+#include "maths.h"
+
+namespace squish {
+
+/*! @brief Represents a set of block colours
+*/
+class ColourSet
+{
+public:
+	ColourSet( u8 const* rgba, int mask, int flags );
+
+	int GetCount() const { return m_count; }
+	Vec3 const* GetPoints() const { return m_points; }
+	float const* GetWeights() const { return m_weights; }
+	bool IsTransparent() const { return m_transparent; }
+
+	void RemapIndices( u8 const* source, u8* target ) const;
+
+private:
+	int m_count;
+	Vec3 m_points[16];
+	float m_weights[16];
+	int m_remap[16];
+	bool m_transparent;
+};
+
+} // namespace sqish
+
+#endif // ndef SQUISH_COLOURSET_H

+ 49 - 0
Source/ThirdParty/libsquish/config.h

@@ -0,0 +1,49 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_CONFIG_H
+#define SQUISH_CONFIG_H
+
+// Set to 1 when building squish to use Altivec instructions.
+#ifndef SQUISH_USE_ALTIVEC
+#define SQUISH_USE_ALTIVEC 0
+#endif
+
+// Set to 1 or 2 when building squish to use SSE or SSE2 instructions.
+#ifndef SQUISH_USE_SSE
+#define SQUISH_USE_SSE 0
+#endif
+
+// Internally set SQUISH_USE_SIMD when either Altivec or SSE is available.
+#if SQUISH_USE_ALTIVEC && SQUISH_USE_SSE
+#error "Cannot enable both Altivec and SSE!"
+#endif
+#if SQUISH_USE_ALTIVEC || SQUISH_USE_SSE
+#define SQUISH_USE_SIMD 1
+#else
+#define SQUISH_USE_SIMD 0
+#endif
+
+#endif // ndef SQUISH_CONFIG_H

+ 259 - 0
Source/ThirdParty/libsquish/maths.cpp

@@ -0,0 +1,259 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+/*! @file
+
+	The symmetric eigensystem solver algorithm is from 
+	http://www.geometrictools.com/Documentation/EigenSymmetric3x3.pdf
+*/
+
+#include "maths.h"
+#include "simd.h"
+#include <cfloat>
+
+namespace squish {
+
+Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights )
+{
+	// compute the centroid
+	float total = 0.0f;
+	Vec3 centroid( 0.0f );
+	for( int i = 0; i < n; ++i )
+	{
+		total += weights[i];
+		centroid += weights[i]*points[i];
+	}
+	if( total > FLT_EPSILON )
+		centroid /= total;
+
+	// accumulate the covariance matrix
+	Sym3x3 covariance( 0.0f );
+	for( int i = 0; i < n; ++i )
+	{
+		Vec3 a = points[i] - centroid;
+		Vec3 b = weights[i]*a;
+		
+		covariance[0] += a.X()*b.X();
+		covariance[1] += a.X()*b.Y();
+		covariance[2] += a.X()*b.Z();
+		covariance[3] += a.Y()*b.Y();
+		covariance[4] += a.Y()*b.Z();
+		covariance[5] += a.Z()*b.Z();
+	}
+	
+	// return it
+	return covariance;
+}
+
+#if 0
+
+static Vec3 GetMultiplicity1Evector( Sym3x3 const& matrix, float evalue )
+{
+	// compute M
+	Sym3x3 m;
+	m[0] = matrix[0] - evalue;
+	m[1] = matrix[1];
+	m[2] = matrix[2];
+	m[3] = matrix[3] - evalue;
+	m[4] = matrix[4];
+	m[5] = matrix[5] - evalue;
+
+	// compute U
+	Sym3x3 u;
+	u[0] = m[3]*m[5] - m[4]*m[4];
+	u[1] = m[2]*m[4] - m[1]*m[5];
+	u[2] = m[1]*m[4] - m[2]*m[3];
+	u[3] = m[0]*m[5] - m[2]*m[2];
+	u[4] = m[1]*m[2] - m[4]*m[0];
+	u[5] = m[0]*m[3] - m[1]*m[1];
+
+	// find the largest component
+	float mc = std::fabs( u[0] );
+	int mi = 0;
+	for( int i = 1; i < 6; ++i )
+	{
+		float c = std::fabs( u[i] );
+		if( c > mc )
+		{
+			mc = c;
+			mi = i;
+		}
+	}
+
+	// pick the column with this component
+	switch( mi )
+	{
+	case 0:
+		return Vec3( u[0], u[1], u[2] );
+
+	case 1:
+	case 3:
+		return Vec3( u[1], u[3], u[4] );
+
+	default:
+		return Vec3( u[2], u[4], u[5] );
+	}
+}
+
+static Vec3 GetMultiplicity2Evector( Sym3x3 const& matrix, float evalue )
+{
+	// compute M
+	Sym3x3 m;
+	m[0] = matrix[0] - evalue;
+	m[1] = matrix[1];
+	m[2] = matrix[2];
+	m[3] = matrix[3] - evalue;
+	m[4] = matrix[4];
+	m[5] = matrix[5] - evalue;
+
+	// find the largest component
+	float mc = std::fabs( m[0] );
+	int mi = 0;
+	for( int i = 1; i < 6; ++i )
+	{
+		float c = std::fabs( m[i] );
+		if( c > mc )
+		{
+			mc = c;
+			mi = i;
+		}
+	}
+
+	// pick the first eigenvector based on this index
+	switch( mi )
+	{
+	case 0:
+	case 1:
+		return Vec3( -m[1], m[0], 0.0f );
+
+	case 2:
+		return Vec3( m[2], 0.0f, -m[0] );
+
+	case 3:
+	case 4:
+		return Vec3( 0.0f, -m[4], m[3] );
+
+	default:
+		return Vec3( 0.0f, -m[5], m[4] );
+	}
+}
+
+Vec3 ComputePrincipleComponent( Sym3x3 const& matrix )
+{
+	// compute the cubic coefficients
+	float c0 = matrix[0]*matrix[3]*matrix[5] 
+		+ 2.0f*matrix[1]*matrix[2]*matrix[4] 
+		- matrix[0]*matrix[4]*matrix[4] 
+		- matrix[3]*matrix[2]*matrix[2] 
+		- matrix[5]*matrix[1]*matrix[1];
+	float c1 = matrix[0]*matrix[3] + matrix[0]*matrix[5] + matrix[3]*matrix[5]
+		- matrix[1]*matrix[1] - matrix[2]*matrix[2] - matrix[4]*matrix[4];
+	float c2 = matrix[0] + matrix[3] + matrix[5];
+
+	// compute the quadratic coefficients
+	float a = c1 - ( 1.0f/3.0f )*c2*c2;
+	float b = ( -2.0f/27.0f )*c2*c2*c2 + ( 1.0f/3.0f )*c1*c2 - c0;
+
+	// compute the root count check
+	float Q = 0.25f*b*b + ( 1.0f/27.0f )*a*a*a;
+
+	// test the multiplicity
+	if( FLT_EPSILON < Q )
+	{
+		// only one root, which implies we have a multiple of the identity
+        return Vec3( 1.0f );
+	}
+	else if( Q < -FLT_EPSILON )
+	{
+		// three distinct roots
+		float theta = std::atan2( std::sqrt( -Q ), -0.5f*b );
+		float rho = std::sqrt( 0.25f*b*b - Q );
+
+		float rt = std::pow( rho, 1.0f/3.0f );
+		float ct = std::cos( theta/3.0f );
+		float st = std::sin( theta/3.0f );
+
+		float l1 = ( 1.0f/3.0f )*c2 + 2.0f*rt*ct;
+		float l2 = ( 1.0f/3.0f )*c2 - rt*( ct + ( float )sqrt( 3.0f )*st );
+		float l3 = ( 1.0f/3.0f )*c2 - rt*( ct - ( float )sqrt( 3.0f )*st );
+
+		// pick the larger
+		if( std::fabs( l2 ) > std::fabs( l1 ) )
+			l1 = l2;
+		if( std::fabs( l3 ) > std::fabs( l1 ) )
+			l1 = l3;
+
+		// get the eigenvector
+		return GetMultiplicity1Evector( matrix, l1 );
+	}
+	else // if( -FLT_EPSILON <= Q && Q <= FLT_EPSILON )
+	{
+		// two roots
+		float rt;
+		if( b < 0.0f )
+			rt = -std::pow( -0.5f*b, 1.0f/3.0f );
+		else
+			rt = std::pow( 0.5f*b, 1.0f/3.0f );
+		
+		float l1 = ( 1.0f/3.0f )*c2 + rt;		// repeated
+		float l2 = ( 1.0f/3.0f )*c2 - 2.0f*rt;
+		
+		// get the eigenvector
+		if( std::fabs( l1 ) > std::fabs( l2 ) )
+			return GetMultiplicity2Evector( matrix, l1 );
+		else
+			return GetMultiplicity1Evector( matrix, l2 );
+	}
+}
+
+#else
+
+#define POWER_ITERATION_COUNT 	8
+
+Vec3 ComputePrincipleComponent( Sym3x3 const& matrix )
+{
+	Vec4 const row0( matrix[0], matrix[1], matrix[2], 0.0f );
+	Vec4 const row1( matrix[1], matrix[3], matrix[4], 0.0f );
+	Vec4 const row2( matrix[2], matrix[4], matrix[5], 0.0f );
+	Vec4 v = VEC4_CONST( 1.0f );
+	for( int i = 0; i < POWER_ITERATION_COUNT; ++i )
+	{
+		// matrix multiply
+		Vec4 w = row0*v.SplatX();
+		w = MultiplyAdd(row1, v.SplatY(), w);
+		w = MultiplyAdd(row2, v.SplatZ(), w);
+
+		// get max component from xyz in all channels
+		Vec4 a = Max(w.SplatX(), Max(w.SplatY(), w.SplatZ()));
+
+		// divide through and advance
+		v = w*Reciprocal(a);
+	}
+	return v.GetVec3();
+}
+
+#endif
+
+} // namespace squish

+ 233 - 0
Source/ThirdParty/libsquish/maths.h

@@ -0,0 +1,233 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_MATHS_H
+#define SQUISH_MATHS_H
+
+#include <cmath>
+#include <algorithm>
+#include "config.h"
+
+namespace squish {
+
+class Vec3
+{
+public:
+	typedef Vec3 const& Arg;
+
+	Vec3()
+	{
+	}
+
+	explicit Vec3( float s )
+	{
+		m_x = s;
+		m_y = s;
+		m_z = s;
+	}
+
+	Vec3( float x, float y, float z )
+	{
+		m_x = x;
+		m_y = y;
+		m_z = z;
+	}
+	
+	float X() const { return m_x; }
+	float Y() const { return m_y; }
+	float Z() const { return m_z; }
+	
+	Vec3 operator-() const
+	{
+		return Vec3( -m_x, -m_y, -m_z );
+	}
+	
+	Vec3& operator+=( Arg v )
+	{
+		m_x += v.m_x;
+		m_y += v.m_y;
+		m_z += v.m_z;
+		return *this;
+	}
+	
+	Vec3& operator-=( Arg v )
+	{
+		m_x -= v.m_x;
+		m_y -= v.m_y;
+		m_z -= v.m_z;
+		return *this;
+	}
+	
+	Vec3& operator*=( Arg v )
+	{
+		m_x *= v.m_x;
+		m_y *= v.m_y;
+		m_z *= v.m_z;
+		return *this;
+	}
+	
+	Vec3& operator*=( float s )
+	{
+		m_x *= s;
+		m_y *= s;
+		m_z *= s;
+		return *this;
+	}
+	
+	Vec3& operator/=( Arg v )
+	{
+		m_x /= v.m_x;
+		m_y /= v.m_y;
+		m_z /= v.m_z;
+		return *this;
+	}
+	
+	Vec3& operator/=( float s )
+	{
+		float t = 1.0f/s;
+		m_x *= t;
+		m_y *= t;
+		m_z *= t;
+		return *this;
+	}
+	
+	friend Vec3 operator+( Arg left, Arg right )
+	{
+		Vec3 copy( left );
+		return copy += right;
+	}
+	
+	friend Vec3 operator-( Arg left, Arg right )
+	{
+		Vec3 copy( left );
+		return copy -= right;
+	}
+	
+	friend Vec3 operator*( Arg left, Arg right )
+	{
+		Vec3 copy( left );
+		return copy *= right;
+	}
+	
+	friend Vec3 operator*( Arg left, float right )
+	{
+		Vec3 copy( left );
+		return copy *= right;
+	}
+	
+	friend Vec3 operator*( float left, Arg right )
+	{
+		Vec3 copy( right );
+		return copy *= left;
+	}
+	
+	friend Vec3 operator/( Arg left, Arg right )
+	{
+		Vec3 copy( left );
+		return copy /= right;
+	}
+	
+	friend Vec3 operator/( Arg left, float right )
+	{
+		Vec3 copy( left );
+		return copy /= right;
+	}
+	
+	friend float Dot( Arg left, Arg right )
+	{
+		return left.m_x*right.m_x + left.m_y*right.m_y + left.m_z*right.m_z;
+	}
+	
+	friend Vec3 Min( Arg left, Arg right )
+	{
+		return Vec3(
+			std::min( left.m_x, right.m_x ), 
+			std::min( left.m_y, right.m_y ), 
+			std::min( left.m_z, right.m_z )
+		);
+	}
+
+	friend Vec3 Max( Arg left, Arg right )
+	{
+		return Vec3(
+			std::max( left.m_x, right.m_x ), 
+			std::max( left.m_y, right.m_y ), 
+			std::max( left.m_z, right.m_z )
+		);
+	}
+
+	friend Vec3 Truncate( Arg v )
+	{
+		return Vec3(
+			v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ), 
+			v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ), 
+			v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z )
+		);
+	}
+
+private:
+	float m_x;
+	float m_y;
+	float m_z;
+};
+
+inline float LengthSquared( Vec3::Arg v )
+{
+	return Dot( v, v );
+}
+
+class Sym3x3
+{
+public:
+	Sym3x3()
+	{
+	}
+
+	Sym3x3( float s )
+	{
+		for( int i = 0; i < 6; ++i )
+			m_x[i] = s;
+	}
+
+	float operator[]( int index ) const
+	{
+		return m_x[index];
+	}
+
+	float& operator[]( int index )
+	{
+		return m_x[index];
+	}
+
+private:
+	float m_x[6];
+};
+
+Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights );
+Vec3 ComputePrincipleComponent( Sym3x3 const& matrix );
+
+} // namespace squish
+
+#endif // ndef SQUISH_MATHS_H

+ 201 - 0
Source/ThirdParty/libsquish/rangefit.cpp

@@ -0,0 +1,201 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#include "rangefit.h"
+#include "colourset.h"
+#include "colourblock.h"
+#include <cfloat>
+
+namespace squish {
+
+RangeFit::RangeFit( ColourSet const* colours, int flags, float* metric ) 
+  : ColourFit( colours, flags )
+{
+	// initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)
+	if( metric )
+		m_metric = Vec3( metric[0], metric[1], metric[2] );
+	else
+		m_metric = Vec3( 1.0f );	
+
+	// initialise the best error
+	m_besterror = FLT_MAX;
+
+	// cache some values
+	int const count = m_colours->GetCount();
+	Vec3 const* values = m_colours->GetPoints();
+	float const* weights = m_colours->GetWeights();
+	
+	// get the covariance matrix
+	Sym3x3 covariance = ComputeWeightedCovariance( count, values, weights );
+	
+	// compute the principle component
+	Vec3 principle = ComputePrincipleComponent( covariance );
+
+	// get the min and max range as the codebook endpoints
+	Vec3 start( 0.0f );
+	Vec3 end( 0.0f );
+	if( count > 0 )
+	{
+		float min, max;
+		
+		// compute the range
+		start = end = values[0];
+		min = max = Dot( values[0], principle );
+		for( int i = 1; i < count; ++i )
+		{
+			float val = Dot( values[i], principle );
+			if( val < min )
+			{
+				start = values[i];
+				min = val;
+			}
+			else if( val > max )
+			{
+				end = values[i];
+				max = val;
+			}
+		}
+	}
+			
+	// clamp the output to [0, 1]
+	Vec3 const one( 1.0f );
+	Vec3 const zero( 0.0f );
+	start = Min( one, Max( zero, start ) );
+	end = Min( one, Max( zero, end ) );
+
+	// clamp to the grid and save
+	Vec3 const grid( 31.0f, 63.0f, 31.0f );
+	Vec3 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f );
+	Vec3 const half( 0.5f );
+	m_start = Truncate( grid*start + half )*gridrcp;
+	m_end = Truncate( grid*end + half )*gridrcp;
+}
+
+void RangeFit::Compress3( void* block )
+{
+	// cache some values
+	int const count = m_colours->GetCount();
+	Vec3 const* values = m_colours->GetPoints();
+	
+	// create a codebook
+	Vec3 codes[3];
+	codes[0] = m_start;
+	codes[1] = m_end;
+	codes[2] = 0.5f*m_start + 0.5f*m_end;
+
+	// match each point to the closest code
+	u8 closest[16];
+	float error = 0.0f;
+	for( int i = 0; i < count; ++i )
+	{
+		// find the closest code
+		float dist = FLT_MAX;
+		int idx = 0;
+		for( int j = 0; j < 3; ++j )
+		{
+			float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
+			if( d < dist )
+			{
+				dist = d;
+				idx = j;
+			}
+		}
+		
+		// save the index
+		closest[i] = ( u8 )idx;
+		
+		// accumulate the error
+		error += dist;
+	}
+	
+	// save this scheme if it wins
+	if( error < m_besterror )
+	{
+		// remap the indices
+		u8 indices[16];
+		m_colours->RemapIndices( closest, indices );
+		
+		// save the block
+		WriteColourBlock3( m_start, m_end, indices, block );
+		
+		// save the error
+		m_besterror = error;
+	}
+}
+
+void RangeFit::Compress4( void* block )
+{
+	// cache some values
+	int const count = m_colours->GetCount();
+	Vec3 const* values = m_colours->GetPoints();
+	
+	// create a codebook
+	Vec3 codes[4];
+	codes[0] = m_start;
+	codes[1] = m_end;
+	codes[2] = ( 2.0f/3.0f )*m_start + ( 1.0f/3.0f )*m_end;
+	codes[3] = ( 1.0f/3.0f )*m_start + ( 2.0f/3.0f )*m_end;
+
+	// match each point to the closest code
+	u8 closest[16];
+	float error = 0.0f;
+	for( int i = 0; i < count; ++i )
+	{
+		// find the closest code
+		float dist = FLT_MAX;
+		int idx = 0;
+		for( int j = 0; j < 4; ++j )
+		{
+			float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
+			if( d < dist )
+			{
+				dist = d;
+				idx = j;
+			}
+		}
+		
+		// save the index
+		closest[i] = ( u8 )idx;
+		
+		// accumulate the error
+		error += dist;
+	}
+	
+	// save this scheme if it wins
+	if( error < m_besterror )
+	{
+		// remap the indices
+		u8 indices[16];
+		m_colours->RemapIndices( closest, indices );
+		
+		// save the block
+		WriteColourBlock4( m_start, m_end, indices, block );
+
+		// save the error
+		m_besterror = error;
+	}
+}
+
+} // namespace squish

+ 54 - 0
Source/ThirdParty/libsquish/rangefit.h

@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_RANGEFIT_H
+#define SQUISH_RANGEFIT_H
+
+#include "squish.h"
+#include "colourfit.h"
+#include "maths.h"
+
+namespace squish {
+
+class ColourSet;
+
+class RangeFit : public ColourFit
+{
+public:
+	RangeFit( ColourSet const* colours, int flags, float* metric );
+	
+private:
+	virtual void Compress3( void* block );
+	virtual void Compress4( void* block );
+	
+	Vec3 m_metric;
+	Vec3 m_start;
+	Vec3 m_end;
+	float m_besterror;
+};
+
+} // squish
+
+#endif // ndef SQUISH_RANGEFIT_H

+ 32 - 0
Source/ThirdParty/libsquish/simd.h

@@ -0,0 +1,32 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_SIMD_H
+#define SQUISH_SIMD_H
+
+#include "maths.h"
+#include "simd_float.h"
+
+#endif // ndef SQUISH_SIMD_H

+ 183 - 0
Source/ThirdParty/libsquish/simd_float.h

@@ -0,0 +1,183 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_SIMD_FLOAT_H
+#define SQUISH_SIMD_FLOAT_H
+
+#include <algorithm>
+
+namespace squish {
+
+#define VEC4_CONST( X ) Vec4( X )
+
+class Vec4
+{
+public:
+	typedef Vec4 const& Arg;
+
+	Vec4() {}
+		
+	explicit Vec4( float s )
+	  : m_x( s ),
+		m_y( s ),
+		m_z( s ),
+		m_w( s )
+	{
+	}
+	
+	Vec4( float x, float y, float z, float w )
+	  : m_x( x ),
+		m_y( y ),
+		m_z( z ),
+		m_w( w )
+	{
+	}
+	
+	Vec3 GetVec3() const
+	{
+		return Vec3( m_x, m_y, m_z );
+	}
+	
+	Vec4 SplatX() const { return Vec4( m_x ); }
+	Vec4 SplatY() const { return Vec4( m_y ); }
+	Vec4 SplatZ() const { return Vec4( m_z ); }
+	Vec4 SplatW() const { return Vec4( m_w ); }
+
+	Vec4& operator+=( Arg v )
+	{
+		m_x += v.m_x;
+		m_y += v.m_y;
+		m_z += v.m_z;
+		m_w += v.m_w;
+		return *this;
+	}
+	
+	Vec4& operator-=( Arg v )
+	{
+		m_x -= v.m_x;
+		m_y -= v.m_y;
+		m_z -= v.m_z;
+		m_w -= v.m_w;
+		return *this;
+	}
+	
+	Vec4& operator*=( Arg v )
+	{
+		m_x *= v.m_x;
+		m_y *= v.m_y;
+		m_z *= v.m_z;
+		m_w *= v.m_w;
+		return *this;
+	}
+	
+	friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right  )
+	{
+		Vec4 copy( left );
+		return copy += right;
+	}
+	
+	friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right  )
+	{
+		Vec4 copy( left );
+		return copy -= right;
+	}
+	
+	friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right  )
+	{
+		Vec4 copy( left );
+		return copy *= right;
+	}
+	
+	//! Returns a*b + c
+	friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
+	{
+		return a*b + c;
+	}
+	
+	//! Returns -( a*b - c )
+	friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
+	{
+		return c - a*b;
+	}
+	
+	friend Vec4 Reciprocal( Vec4::Arg v )
+	{
+		return Vec4( 
+			1.0f/v.m_x, 
+			1.0f/v.m_y, 
+			1.0f/v.m_z, 
+			1.0f/v.m_w 
+		);
+	}
+	
+	friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
+	{
+		return Vec4( 
+			std::min( left.m_x, right.m_x ), 
+			std::min( left.m_y, right.m_y ), 
+			std::min( left.m_z, right.m_z ), 
+			std::min( left.m_w, right.m_w ) 
+		);
+	}
+	
+	friend Vec4 Max( Vec4::Arg left, Vec4::Arg right )
+	{
+		return Vec4( 
+			std::max( left.m_x, right.m_x ), 
+			std::max( left.m_y, right.m_y ), 
+			std::max( left.m_z, right.m_z ), 
+			std::max( left.m_w, right.m_w ) 
+		);
+	}
+	
+	friend Vec4 Truncate( Vec4::Arg v )
+	{
+		return Vec4(
+			v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ), 
+			v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ), 
+			v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z ),
+			v.m_w > 0.0f ? std::floor( v.m_w ) : std::ceil( v.m_w )
+		);
+	}
+	
+	friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right ) 
+	{
+		return left.m_x < right.m_x
+			|| left.m_y < right.m_y
+			|| left.m_z < right.m_z
+			|| left.m_w < right.m_w;
+	}
+	
+private:
+	float m_x;
+	float m_y;
+	float m_z;
+	float m_w;
+};
+
+} // namespace squish
+
+#endif // ndef SQUISH_SIMD_FLOAT_H
+

+ 172 - 0
Source/ThirdParty/libsquish/singlecolourfit.cpp

@@ -0,0 +1,172 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#include "singlecolourfit.h"
+#include "colourset.h"
+#include "colourblock.h"
+
+namespace squish {
+
+struct SourceBlock
+{
+	u8 start;
+	u8 end;
+	u8 error;
+};
+
+struct SingleColourLookup
+{
+	SourceBlock sources[2];
+};
+
+#include "singlecolourlookup.inl"
+
+static int FloatToInt( float a, int limit )
+{
+	// use ANSI round-to-zero behaviour to get round-to-nearest
+	int i = ( int )( a + 0.5f );
+
+	// clamp to the limit
+	if( i < 0 )
+		i = 0;
+	else if( i > limit )
+		i = limit; 
+
+	// done
+	return i;
+}
+
+SingleColourFit::SingleColourFit( ColourSet const* colours, int flags )
+  : ColourFit( colours, flags )
+{
+	// grab the single colour
+	Vec3 const* values = m_colours->GetPoints();
+	m_colour[0] = ( u8 )FloatToInt( 255.0f*values->X(), 255 );
+	m_colour[1] = ( u8 )FloatToInt( 255.0f*values->Y(), 255 );
+	m_colour[2] = ( u8 )FloatToInt( 255.0f*values->Z(), 255 );
+		
+	// initialise the best error
+	m_besterror = INT_MAX;
+}
+
+void SingleColourFit::Compress3( void* block )
+{
+	// build the table of lookups
+	SingleColourLookup const* const lookups[] = 
+	{
+		lookup_5_3, 
+		lookup_6_3, 
+		lookup_5_3
+	};
+	
+	// find the best end-points and index
+	ComputeEndPoints( lookups );
+	
+	// build the block if we win
+	if( m_error < m_besterror )
+	{
+		// remap the indices
+		u8 indices[16];
+		m_colours->RemapIndices( &m_index, indices );
+		
+		// save the block
+		WriteColourBlock3( m_start, m_end, indices, block );
+
+		// save the error
+		m_besterror = m_error;
+	}
+}
+
+void SingleColourFit::Compress4( void* block )
+{
+	// build the table of lookups
+	SingleColourLookup const* const lookups[] = 
+	{
+		lookup_5_4, 
+		lookup_6_4, 
+		lookup_5_4
+	};
+	
+	// find the best end-points and index
+	ComputeEndPoints( lookups );
+	
+	// build the block if we win
+	if( m_error < m_besterror )
+	{
+		// remap the indices
+		u8 indices[16];
+		m_colours->RemapIndices( &m_index, indices );
+		
+		// save the block
+		WriteColourBlock4( m_start, m_end, indices, block );
+
+		// save the error
+		m_besterror = m_error;
+	}
+}
+
+void SingleColourFit::ComputeEndPoints( SingleColourLookup const* const* lookups )
+{
+	// check each index combination (endpoint or intermediate)
+	m_error = INT_MAX;
+	for( int index = 0; index < 2; ++index )
+	{
+		// check the error for this codebook index
+		SourceBlock const* sources[3];
+		int error = 0;
+		for( int channel = 0; channel < 3; ++channel )
+		{
+			// grab the lookup table and index for this channel
+			SingleColourLookup const* lookup = lookups[channel];
+			int target = m_colour[channel];
+			
+			// store a pointer to the source for this channel
+			sources[channel] = lookup[target].sources + index;
+			
+			// accumulate the error
+			int diff = sources[channel]->error;
+			error += diff*diff;			
+		}
+		
+		// keep it if the error is lower
+		if( error < m_error )
+		{
+			m_start = Vec3(
+				( float )sources[0]->start/31.0f, 
+				( float )sources[1]->start/63.0f, 
+				( float )sources[2]->start/31.0f
+			);
+			m_end = Vec3(
+				( float )sources[0]->end/31.0f, 
+				( float )sources[1]->end/63.0f, 
+				( float )sources[2]->end/31.0f
+			);
+			m_index = ( u8 )( 2*index );
+			m_error = error;
+		}
+	}
+}
+
+} // namespace squish

+ 58 - 0
Source/ThirdParty/libsquish/singlecolourfit.h

@@ -0,0 +1,58 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_SINGLECOLOURFIT_H
+#define SQUISH_SINGLECOLOURFIT_H
+
+#include "squish.h"
+#include "colourfit.h"
+
+namespace squish {
+
+class ColourSet;
+struct SingleColourLookup;
+
+class SingleColourFit : public ColourFit
+{
+public:
+	SingleColourFit( ColourSet const* colours, int flags );
+	
+private:
+	virtual void Compress3( void* block );
+	virtual void Compress4( void* block );
+	
+	void ComputeEndPoints( SingleColourLookup const* const* lookups );
+	
+	u8 m_colour[3];
+	Vec3 m_start;
+	Vec3 m_end;
+	u8 m_index;
+	int m_error;
+	int m_besterror;
+};
+
+} // namespace squish
+
+#endif // ndef SQUISH_SINGLECOLOURFIT_H

+ 1064 - 0
Source/ThirdParty/libsquish/singlecolourlookup.inl

@@ -0,0 +1,1064 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+
+static SingleColourLookup const lookup_5_3[] = 
+{
+	{ { { 0, 0, 0 }, { 0, 0, 0 } } },
+	{ { { 0, 0, 1 }, { 0, 0, 1 } } },
+	{ { { 0, 0, 2 }, { 0, 0, 2 } } },
+	{ { { 0, 0, 3 }, { 0, 1, 1 } } },
+	{ { { 0, 0, 4 }, { 0, 1, 0 } } },
+	{ { { 1, 0, 3 }, { 0, 1, 1 } } },
+	{ { { 1, 0, 2 }, { 0, 1, 2 } } },
+	{ { { 1, 0, 1 }, { 0, 2, 1 } } },
+	{ { { 1, 0, 0 }, { 0, 2, 0 } } },
+	{ { { 1, 0, 1 }, { 0, 2, 1 } } },
+	{ { { 1, 0, 2 }, { 0, 2, 2 } } },
+	{ { { 1, 0, 3 }, { 0, 3, 1 } } },
+	{ { { 1, 0, 4 }, { 0, 3, 0 } } },
+	{ { { 2, 0, 3 }, { 0, 3, 1 } } },
+	{ { { 2, 0, 2 }, { 0, 3, 2 } } },
+	{ { { 2, 0, 1 }, { 0, 4, 1 } } },
+	{ { { 2, 0, 0 }, { 0, 4, 0 } } },
+	{ { { 2, 0, 1 }, { 0, 4, 1 } } },
+	{ { { 2, 0, 2 }, { 0, 4, 2 } } },
+	{ { { 2, 0, 3 }, { 0, 5, 1 } } },
+	{ { { 2, 0, 4 }, { 0, 5, 0 } } },
+	{ { { 3, 0, 3 }, { 0, 5, 1 } } },
+	{ { { 3, 0, 2 }, { 0, 5, 2 } } },
+	{ { { 3, 0, 1 }, { 0, 6, 1 } } },
+	{ { { 3, 0, 0 }, { 0, 6, 0 } } },
+	{ { { 3, 0, 1 }, { 0, 6, 1 } } },
+	{ { { 3, 0, 2 }, { 0, 6, 2 } } },
+	{ { { 3, 0, 3 }, { 0, 7, 1 } } },
+	{ { { 3, 0, 4 }, { 0, 7, 0 } } },
+	{ { { 4, 0, 4 }, { 0, 7, 1 } } },
+	{ { { 4, 0, 3 }, { 0, 7, 2 } } },
+	{ { { 4, 0, 2 }, { 1, 7, 1 } } },
+	{ { { 4, 0, 1 }, { 1, 7, 0 } } },
+	{ { { 4, 0, 0 }, { 0, 8, 0 } } },
+	{ { { 4, 0, 1 }, { 0, 8, 1 } } },
+	{ { { 4, 0, 2 }, { 2, 7, 1 } } },
+	{ { { 4, 0, 3 }, { 2, 7, 0 } } },
+	{ { { 4, 0, 4 }, { 0, 9, 0 } } },
+	{ { { 5, 0, 3 }, { 0, 9, 1 } } },
+	{ { { 5, 0, 2 }, { 3, 7, 1 } } },
+	{ { { 5, 0, 1 }, { 3, 7, 0 } } },
+	{ { { 5, 0, 0 }, { 0, 10, 0 } } },
+	{ { { 5, 0, 1 }, { 0, 10, 1 } } },
+	{ { { 5, 0, 2 }, { 0, 10, 2 } } },
+	{ { { 5, 0, 3 }, { 0, 11, 1 } } },
+	{ { { 5, 0, 4 }, { 0, 11, 0 } } },
+	{ { { 6, 0, 3 }, { 0, 11, 1 } } },
+	{ { { 6, 0, 2 }, { 0, 11, 2 } } },
+	{ { { 6, 0, 1 }, { 0, 12, 1 } } },
+	{ { { 6, 0, 0 }, { 0, 12, 0 } } },
+	{ { { 6, 0, 1 }, { 0, 12, 1 } } },
+	{ { { 6, 0, 2 }, { 0, 12, 2 } } },
+	{ { { 6, 0, 3 }, { 0, 13, 1 } } },
+	{ { { 6, 0, 4 }, { 0, 13, 0 } } },
+	{ { { 7, 0, 3 }, { 0, 13, 1 } } },
+	{ { { 7, 0, 2 }, { 0, 13, 2 } } },
+	{ { { 7, 0, 1 }, { 0, 14, 1 } } },
+	{ { { 7, 0, 0 }, { 0, 14, 0 } } },
+	{ { { 7, 0, 1 }, { 0, 14, 1 } } },
+	{ { { 7, 0, 2 }, { 0, 14, 2 } } },
+	{ { { 7, 0, 3 }, { 0, 15, 1 } } },
+	{ { { 7, 0, 4 }, { 0, 15, 0 } } },
+	{ { { 8, 0, 4 }, { 0, 15, 1 } } },
+	{ { { 8, 0, 3 }, { 0, 15, 2 } } },
+	{ { { 8, 0, 2 }, { 1, 15, 1 } } },
+	{ { { 8, 0, 1 }, { 1, 15, 0 } } },
+	{ { { 8, 0, 0 }, { 0, 16, 0 } } },
+	{ { { 8, 0, 1 }, { 0, 16, 1 } } },
+	{ { { 8, 0, 2 }, { 2, 15, 1 } } },
+	{ { { 8, 0, 3 }, { 2, 15, 0 } } },
+	{ { { 8, 0, 4 }, { 0, 17, 0 } } },
+	{ { { 9, 0, 3 }, { 0, 17, 1 } } },
+	{ { { 9, 0, 2 }, { 3, 15, 1 } } },
+	{ { { 9, 0, 1 }, { 3, 15, 0 } } },
+	{ { { 9, 0, 0 }, { 0, 18, 0 } } },
+	{ { { 9, 0, 1 }, { 0, 18, 1 } } },
+	{ { { 9, 0, 2 }, { 0, 18, 2 } } },
+	{ { { 9, 0, 3 }, { 0, 19, 1 } } },
+	{ { { 9, 0, 4 }, { 0, 19, 0 } } },
+	{ { { 10, 0, 3 }, { 0, 19, 1 } } },
+	{ { { 10, 0, 2 }, { 0, 19, 2 } } },
+	{ { { 10, 0, 1 }, { 0, 20, 1 } } },
+	{ { { 10, 0, 0 }, { 0, 20, 0 } } },
+	{ { { 10, 0, 1 }, { 0, 20, 1 } } },
+	{ { { 10, 0, 2 }, { 0, 20, 2 } } },
+	{ { { 10, 0, 3 }, { 0, 21, 1 } } },
+	{ { { 10, 0, 4 }, { 0, 21, 0 } } },
+	{ { { 11, 0, 3 }, { 0, 21, 1 } } },
+	{ { { 11, 0, 2 }, { 0, 21, 2 } } },
+	{ { { 11, 0, 1 }, { 0, 22, 1 } } },
+	{ { { 11, 0, 0 }, { 0, 22, 0 } } },
+	{ { { 11, 0, 1 }, { 0, 22, 1 } } },
+	{ { { 11, 0, 2 }, { 0, 22, 2 } } },
+	{ { { 11, 0, 3 }, { 0, 23, 1 } } },
+	{ { { 11, 0, 4 }, { 0, 23, 0 } } },
+	{ { { 12, 0, 4 }, { 0, 23, 1 } } },
+	{ { { 12, 0, 3 }, { 0, 23, 2 } } },
+	{ { { 12, 0, 2 }, { 1, 23, 1 } } },
+	{ { { 12, 0, 1 }, { 1, 23, 0 } } },
+	{ { { 12, 0, 0 }, { 0, 24, 0 } } },
+	{ { { 12, 0, 1 }, { 0, 24, 1 } } },
+	{ { { 12, 0, 2 }, { 2, 23, 1 } } },
+	{ { { 12, 0, 3 }, { 2, 23, 0 } } },
+	{ { { 12, 0, 4 }, { 0, 25, 0 } } },
+	{ { { 13, 0, 3 }, { 0, 25, 1 } } },
+	{ { { 13, 0, 2 }, { 3, 23, 1 } } },
+	{ { { 13, 0, 1 }, { 3, 23, 0 } } },
+	{ { { 13, 0, 0 }, { 0, 26, 0 } } },
+	{ { { 13, 0, 1 }, { 0, 26, 1 } } },
+	{ { { 13, 0, 2 }, { 0, 26, 2 } } },
+	{ { { 13, 0, 3 }, { 0, 27, 1 } } },
+	{ { { 13, 0, 4 }, { 0, 27, 0 } } },
+	{ { { 14, 0, 3 }, { 0, 27, 1 } } },
+	{ { { 14, 0, 2 }, { 0, 27, 2 } } },
+	{ { { 14, 0, 1 }, { 0, 28, 1 } } },
+	{ { { 14, 0, 0 }, { 0, 28, 0 } } },
+	{ { { 14, 0, 1 }, { 0, 28, 1 } } },
+	{ { { 14, 0, 2 }, { 0, 28, 2 } } },
+	{ { { 14, 0, 3 }, { 0, 29, 1 } } },
+	{ { { 14, 0, 4 }, { 0, 29, 0 } } },
+	{ { { 15, 0, 3 }, { 0, 29, 1 } } },
+	{ { { 15, 0, 2 }, { 0, 29, 2 } } },
+	{ { { 15, 0, 1 }, { 0, 30, 1 } } },
+	{ { { 15, 0, 0 }, { 0, 30, 0 } } },
+	{ { { 15, 0, 1 }, { 0, 30, 1 } } },
+	{ { { 15, 0, 2 }, { 0, 30, 2 } } },
+	{ { { 15, 0, 3 }, { 0, 31, 1 } } },
+	{ { { 15, 0, 4 }, { 0, 31, 0 } } },
+	{ { { 16, 0, 4 }, { 0, 31, 1 } } },
+	{ { { 16, 0, 3 }, { 0, 31, 2 } } },
+	{ { { 16, 0, 2 }, { 1, 31, 1 } } },
+	{ { { 16, 0, 1 }, { 1, 31, 0 } } },
+	{ { { 16, 0, 0 }, { 4, 28, 0 } } },
+	{ { { 16, 0, 1 }, { 4, 28, 1 } } },
+	{ { { 16, 0, 2 }, { 2, 31, 1 } } },
+	{ { { 16, 0, 3 }, { 2, 31, 0 } } },
+	{ { { 16, 0, 4 }, { 4, 29, 0 } } },
+	{ { { 17, 0, 3 }, { 4, 29, 1 } } },
+	{ { { 17, 0, 2 }, { 3, 31, 1 } } },
+	{ { { 17, 0, 1 }, { 3, 31, 0 } } },
+	{ { { 17, 0, 0 }, { 4, 30, 0 } } },
+	{ { { 17, 0, 1 }, { 4, 30, 1 } } },
+	{ { { 17, 0, 2 }, { 4, 30, 2 } } },
+	{ { { 17, 0, 3 }, { 4, 31, 1 } } },
+	{ { { 17, 0, 4 }, { 4, 31, 0 } } },
+	{ { { 18, 0, 3 }, { 4, 31, 1 } } },
+	{ { { 18, 0, 2 }, { 4, 31, 2 } } },
+	{ { { 18, 0, 1 }, { 5, 31, 1 } } },
+	{ { { 18, 0, 0 }, { 5, 31, 0 } } },
+	{ { { 18, 0, 1 }, { 5, 31, 1 } } },
+	{ { { 18, 0, 2 }, { 5, 31, 2 } } },
+	{ { { 18, 0, 3 }, { 6, 31, 1 } } },
+	{ { { 18, 0, 4 }, { 6, 31, 0 } } },
+	{ { { 19, 0, 3 }, { 6, 31, 1 } } },
+	{ { { 19, 0, 2 }, { 6, 31, 2 } } },
+	{ { { 19, 0, 1 }, { 7, 31, 1 } } },
+	{ { { 19, 0, 0 }, { 7, 31, 0 } } },
+	{ { { 19, 0, 1 }, { 7, 31, 1 } } },
+	{ { { 19, 0, 2 }, { 7, 31, 2 } } },
+	{ { { 19, 0, 3 }, { 8, 31, 1 } } },
+	{ { { 19, 0, 4 }, { 8, 31, 0 } } },
+	{ { { 20, 0, 4 }, { 8, 31, 1 } } },
+	{ { { 20, 0, 3 }, { 8, 31, 2 } } },
+	{ { { 20, 0, 2 }, { 9, 31, 1 } } },
+	{ { { 20, 0, 1 }, { 9, 31, 0 } } },
+	{ { { 20, 0, 0 }, { 12, 28, 0 } } },
+	{ { { 20, 0, 1 }, { 12, 28, 1 } } },
+	{ { { 20, 0, 2 }, { 10, 31, 1 } } },
+	{ { { 20, 0, 3 }, { 10, 31, 0 } } },
+	{ { { 20, 0, 4 }, { 12, 29, 0 } } },
+	{ { { 21, 0, 3 }, { 12, 29, 1 } } },
+	{ { { 21, 0, 2 }, { 11, 31, 1 } } },
+	{ { { 21, 0, 1 }, { 11, 31, 0 } } },
+	{ { { 21, 0, 0 }, { 12, 30, 0 } } },
+	{ { { 21, 0, 1 }, { 12, 30, 1 } } },
+	{ { { 21, 0, 2 }, { 12, 30, 2 } } },
+	{ { { 21, 0, 3 }, { 12, 31, 1 } } },
+	{ { { 21, 0, 4 }, { 12, 31, 0 } } },
+	{ { { 22, 0, 3 }, { 12, 31, 1 } } },
+	{ { { 22, 0, 2 }, { 12, 31, 2 } } },
+	{ { { 22, 0, 1 }, { 13, 31, 1 } } },
+	{ { { 22, 0, 0 }, { 13, 31, 0 } } },
+	{ { { 22, 0, 1 }, { 13, 31, 1 } } },
+	{ { { 22, 0, 2 }, { 13, 31, 2 } } },
+	{ { { 22, 0, 3 }, { 14, 31, 1 } } },
+	{ { { 22, 0, 4 }, { 14, 31, 0 } } },
+	{ { { 23, 0, 3 }, { 14, 31, 1 } } },
+	{ { { 23, 0, 2 }, { 14, 31, 2 } } },
+	{ { { 23, 0, 1 }, { 15, 31, 1 } } },
+	{ { { 23, 0, 0 }, { 15, 31, 0 } } },
+	{ { { 23, 0, 1 }, { 15, 31, 1 } } },
+	{ { { 23, 0, 2 }, { 15, 31, 2 } } },
+	{ { { 23, 0, 3 }, { 16, 31, 1 } } },
+	{ { { 23, 0, 4 }, { 16, 31, 0 } } },
+	{ { { 24, 0, 4 }, { 16, 31, 1 } } },
+	{ { { 24, 0, 3 }, { 16, 31, 2 } } },
+	{ { { 24, 0, 2 }, { 17, 31, 1 } } },
+	{ { { 24, 0, 1 }, { 17, 31, 0 } } },
+	{ { { 24, 0, 0 }, { 20, 28, 0 } } },
+	{ { { 24, 0, 1 }, { 20, 28, 1 } } },
+	{ { { 24, 0, 2 }, { 18, 31, 1 } } },
+	{ { { 24, 0, 3 }, { 18, 31, 0 } } },
+	{ { { 24, 0, 4 }, { 20, 29, 0 } } },
+	{ { { 25, 0, 3 }, { 20, 29, 1 } } },
+	{ { { 25, 0, 2 }, { 19, 31, 1 } } },
+	{ { { 25, 0, 1 }, { 19, 31, 0 } } },
+	{ { { 25, 0, 0 }, { 20, 30, 0 } } },
+	{ { { 25, 0, 1 }, { 20, 30, 1 } } },
+	{ { { 25, 0, 2 }, { 20, 30, 2 } } },
+	{ { { 25, 0, 3 }, { 20, 31, 1 } } },
+	{ { { 25, 0, 4 }, { 20, 31, 0 } } },
+	{ { { 26, 0, 3 }, { 20, 31, 1 } } },
+	{ { { 26, 0, 2 }, { 20, 31, 2 } } },
+	{ { { 26, 0, 1 }, { 21, 31, 1 } } },
+	{ { { 26, 0, 0 }, { 21, 31, 0 } } },
+	{ { { 26, 0, 1 }, { 21, 31, 1 } } },
+	{ { { 26, 0, 2 }, { 21, 31, 2 } } },
+	{ { { 26, 0, 3 }, { 22, 31, 1 } } },
+	{ { { 26, 0, 4 }, { 22, 31, 0 } } },
+	{ { { 27, 0, 3 }, { 22, 31, 1 } } },
+	{ { { 27, 0, 2 }, { 22, 31, 2 } } },
+	{ { { 27, 0, 1 }, { 23, 31, 1 } } },
+	{ { { 27, 0, 0 }, { 23, 31, 0 } } },
+	{ { { 27, 0, 1 }, { 23, 31, 1 } } },
+	{ { { 27, 0, 2 }, { 23, 31, 2 } } },
+	{ { { 27, 0, 3 }, { 24, 31, 1 } } },
+	{ { { 27, 0, 4 }, { 24, 31, 0 } } },
+	{ { { 28, 0, 4 }, { 24, 31, 1 } } },
+	{ { { 28, 0, 3 }, { 24, 31, 2 } } },
+	{ { { 28, 0, 2 }, { 25, 31, 1 } } },
+	{ { { 28, 0, 1 }, { 25, 31, 0 } } },
+	{ { { 28, 0, 0 }, { 28, 28, 0 } } },
+	{ { { 28, 0, 1 }, { 28, 28, 1 } } },
+	{ { { 28, 0, 2 }, { 26, 31, 1 } } },
+	{ { { 28, 0, 3 }, { 26, 31, 0 } } },
+	{ { { 28, 0, 4 }, { 28, 29, 0 } } },
+	{ { { 29, 0, 3 }, { 28, 29, 1 } } },
+	{ { { 29, 0, 2 }, { 27, 31, 1 } } },
+	{ { { 29, 0, 1 }, { 27, 31, 0 } } },
+	{ { { 29, 0, 0 }, { 28, 30, 0 } } },
+	{ { { 29, 0, 1 }, { 28, 30, 1 } } },
+	{ { { 29, 0, 2 }, { 28, 30, 2 } } },
+	{ { { 29, 0, 3 }, { 28, 31, 1 } } },
+	{ { { 29, 0, 4 }, { 28, 31, 0 } } },
+	{ { { 30, 0, 3 }, { 28, 31, 1 } } },
+	{ { { 30, 0, 2 }, { 28, 31, 2 } } },
+	{ { { 30, 0, 1 }, { 29, 31, 1 } } },
+	{ { { 30, 0, 0 }, { 29, 31, 0 } } },
+	{ { { 30, 0, 1 }, { 29, 31, 1 } } },
+	{ { { 30, 0, 2 }, { 29, 31, 2 } } },
+	{ { { 30, 0, 3 }, { 30, 31, 1 } } },
+	{ { { 30, 0, 4 }, { 30, 31, 0 } } },
+	{ { { 31, 0, 3 }, { 30, 31, 1 } } },
+	{ { { 31, 0, 2 }, { 30, 31, 2 } } },
+	{ { { 31, 0, 1 }, { 31, 31, 1 } } },
+	{ { { 31, 0, 0 }, { 31, 31, 0 } } }
+};
+
+static SingleColourLookup const lookup_6_3[] = 
+{
+	{ { { 0, 0, 0 }, { 0, 0, 0 } } },
+	{ { { 0, 0, 1 }, { 0, 1, 1 } } },
+	{ { { 0, 0, 2 }, { 0, 1, 0 } } },
+	{ { { 1, 0, 1 }, { 0, 2, 1 } } },
+	{ { { 1, 0, 0 }, { 0, 2, 0 } } },
+	{ { { 1, 0, 1 }, { 0, 3, 1 } } },
+	{ { { 1, 0, 2 }, { 0, 3, 0 } } },
+	{ { { 2, 0, 1 }, { 0, 4, 1 } } },
+	{ { { 2, 0, 0 }, { 0, 4, 0 } } },
+	{ { { 2, 0, 1 }, { 0, 5, 1 } } },
+	{ { { 2, 0, 2 }, { 0, 5, 0 } } },
+	{ { { 3, 0, 1 }, { 0, 6, 1 } } },
+	{ { { 3, 0, 0 }, { 0, 6, 0 } } },
+	{ { { 3, 0, 1 }, { 0, 7, 1 } } },
+	{ { { 3, 0, 2 }, { 0, 7, 0 } } },
+	{ { { 4, 0, 1 }, { 0, 8, 1 } } },
+	{ { { 4, 0, 0 }, { 0, 8, 0 } } },
+	{ { { 4, 0, 1 }, { 0, 9, 1 } } },
+	{ { { 4, 0, 2 }, { 0, 9, 0 } } },
+	{ { { 5, 0, 1 }, { 0, 10, 1 } } },
+	{ { { 5, 0, 0 }, { 0, 10, 0 } } },
+	{ { { 5, 0, 1 }, { 0, 11, 1 } } },
+	{ { { 5, 0, 2 }, { 0, 11, 0 } } },
+	{ { { 6, 0, 1 }, { 0, 12, 1 } } },
+	{ { { 6, 0, 0 }, { 0, 12, 0 } } },
+	{ { { 6, 0, 1 }, { 0, 13, 1 } } },
+	{ { { 6, 0, 2 }, { 0, 13, 0 } } },
+	{ { { 7, 0, 1 }, { 0, 14, 1 } } },
+	{ { { 7, 0, 0 }, { 0, 14, 0 } } },
+	{ { { 7, 0, 1 }, { 0, 15, 1 } } },
+	{ { { 7, 0, 2 }, { 0, 15, 0 } } },
+	{ { { 8, 0, 1 }, { 0, 16, 1 } } },
+	{ { { 8, 0, 0 }, { 0, 16, 0 } } },
+	{ { { 8, 0, 1 }, { 0, 17, 1 } } },
+	{ { { 8, 0, 2 }, { 0, 17, 0 } } },
+	{ { { 9, 0, 1 }, { 0, 18, 1 } } },
+	{ { { 9, 0, 0 }, { 0, 18, 0 } } },
+	{ { { 9, 0, 1 }, { 0, 19, 1 } } },
+	{ { { 9, 0, 2 }, { 0, 19, 0 } } },
+	{ { { 10, 0, 1 }, { 0, 20, 1 } } },
+	{ { { 10, 0, 0 }, { 0, 20, 0 } } },
+	{ { { 10, 0, 1 }, { 0, 21, 1 } } },
+	{ { { 10, 0, 2 }, { 0, 21, 0 } } },
+	{ { { 11, 0, 1 }, { 0, 22, 1 } } },
+	{ { { 11, 0, 0 }, { 0, 22, 0 } } },
+	{ { { 11, 0, 1 }, { 0, 23, 1 } } },
+	{ { { 11, 0, 2 }, { 0, 23, 0 } } },
+	{ { { 12, 0, 1 }, { 0, 24, 1 } } },
+	{ { { 12, 0, 0 }, { 0, 24, 0 } } },
+	{ { { 12, 0, 1 }, { 0, 25, 1 } } },
+	{ { { 12, 0, 2 }, { 0, 25, 0 } } },
+	{ { { 13, 0, 1 }, { 0, 26, 1 } } },
+	{ { { 13, 0, 0 }, { 0, 26, 0 } } },
+	{ { { 13, 0, 1 }, { 0, 27, 1 } } },
+	{ { { 13, 0, 2 }, { 0, 27, 0 } } },
+	{ { { 14, 0, 1 }, { 0, 28, 1 } } },
+	{ { { 14, 0, 0 }, { 0, 28, 0 } } },
+	{ { { 14, 0, 1 }, { 0, 29, 1 } } },
+	{ { { 14, 0, 2 }, { 0, 29, 0 } } },
+	{ { { 15, 0, 1 }, { 0, 30, 1 } } },
+	{ { { 15, 0, 0 }, { 0, 30, 0 } } },
+	{ { { 15, 0, 1 }, { 0, 31, 1 } } },
+	{ { { 15, 0, 2 }, { 0, 31, 0 } } },
+	{ { { 16, 0, 2 }, { 1, 31, 1 } } },
+	{ { { 16, 0, 1 }, { 1, 31, 0 } } },
+	{ { { 16, 0, 0 }, { 0, 32, 0 } } },
+	{ { { 16, 0, 1 }, { 2, 31, 0 } } },
+	{ { { 16, 0, 2 }, { 0, 33, 0 } } },
+	{ { { 17, 0, 1 }, { 3, 31, 0 } } },
+	{ { { 17, 0, 0 }, { 0, 34, 0 } } },
+	{ { { 17, 0, 1 }, { 4, 31, 0 } } },
+	{ { { 17, 0, 2 }, { 0, 35, 0 } } },
+	{ { { 18, 0, 1 }, { 5, 31, 0 } } },
+	{ { { 18, 0, 0 }, { 0, 36, 0 } } },
+	{ { { 18, 0, 1 }, { 6, 31, 0 } } },
+	{ { { 18, 0, 2 }, { 0, 37, 0 } } },
+	{ { { 19, 0, 1 }, { 7, 31, 0 } } },
+	{ { { 19, 0, 0 }, { 0, 38, 0 } } },
+	{ { { 19, 0, 1 }, { 8, 31, 0 } } },
+	{ { { 19, 0, 2 }, { 0, 39, 0 } } },
+	{ { { 20, 0, 1 }, { 9, 31, 0 } } },
+	{ { { 20, 0, 0 }, { 0, 40, 0 } } },
+	{ { { 20, 0, 1 }, { 10, 31, 0 } } },
+	{ { { 20, 0, 2 }, { 0, 41, 0 } } },
+	{ { { 21, 0, 1 }, { 11, 31, 0 } } },
+	{ { { 21, 0, 0 }, { 0, 42, 0 } } },
+	{ { { 21, 0, 1 }, { 12, 31, 0 } } },
+	{ { { 21, 0, 2 }, { 0, 43, 0 } } },
+	{ { { 22, 0, 1 }, { 13, 31, 0 } } },
+	{ { { 22, 0, 0 }, { 0, 44, 0 } } },
+	{ { { 22, 0, 1 }, { 14, 31, 0 } } },
+	{ { { 22, 0, 2 }, { 0, 45, 0 } } },
+	{ { { 23, 0, 1 }, { 15, 31, 0 } } },
+	{ { { 23, 0, 0 }, { 0, 46, 0 } } },
+	{ { { 23, 0, 1 }, { 0, 47, 1 } } },
+	{ { { 23, 0, 2 }, { 0, 47, 0 } } },
+	{ { { 24, 0, 1 }, { 0, 48, 1 } } },
+	{ { { 24, 0, 0 }, { 0, 48, 0 } } },
+	{ { { 24, 0, 1 }, { 0, 49, 1 } } },
+	{ { { 24, 0, 2 }, { 0, 49, 0 } } },
+	{ { { 25, 0, 1 }, { 0, 50, 1 } } },
+	{ { { 25, 0, 0 }, { 0, 50, 0 } } },
+	{ { { 25, 0, 1 }, { 0, 51, 1 } } },
+	{ { { 25, 0, 2 }, { 0, 51, 0 } } },
+	{ { { 26, 0, 1 }, { 0, 52, 1 } } },
+	{ { { 26, 0, 0 }, { 0, 52, 0 } } },
+	{ { { 26, 0, 1 }, { 0, 53, 1 } } },
+	{ { { 26, 0, 2 }, { 0, 53, 0 } } },
+	{ { { 27, 0, 1 }, { 0, 54, 1 } } },
+	{ { { 27, 0, 0 }, { 0, 54, 0 } } },
+	{ { { 27, 0, 1 }, { 0, 55, 1 } } },
+	{ { { 27, 0, 2 }, { 0, 55, 0 } } },
+	{ { { 28, 0, 1 }, { 0, 56, 1 } } },
+	{ { { 28, 0, 0 }, { 0, 56, 0 } } },
+	{ { { 28, 0, 1 }, { 0, 57, 1 } } },
+	{ { { 28, 0, 2 }, { 0, 57, 0 } } },
+	{ { { 29, 0, 1 }, { 0, 58, 1 } } },
+	{ { { 29, 0, 0 }, { 0, 58, 0 } } },
+	{ { { 29, 0, 1 }, { 0, 59, 1 } } },
+	{ { { 29, 0, 2 }, { 0, 59, 0 } } },
+	{ { { 30, 0, 1 }, { 0, 60, 1 } } },
+	{ { { 30, 0, 0 }, { 0, 60, 0 } } },
+	{ { { 30, 0, 1 }, { 0, 61, 1 } } },
+	{ { { 30, 0, 2 }, { 0, 61, 0 } } },
+	{ { { 31, 0, 1 }, { 0, 62, 1 } } },
+	{ { { 31, 0, 0 }, { 0, 62, 0 } } },
+	{ { { 31, 0, 1 }, { 0, 63, 1 } } },
+	{ { { 31, 0, 2 }, { 0, 63, 0 } } },
+	{ { { 32, 0, 2 }, { 1, 63, 1 } } },
+	{ { { 32, 0, 1 }, { 1, 63, 0 } } },
+	{ { { 32, 0, 0 }, { 16, 48, 0 } } },
+	{ { { 32, 0, 1 }, { 2, 63, 0 } } },
+	{ { { 32, 0, 2 }, { 16, 49, 0 } } },
+	{ { { 33, 0, 1 }, { 3, 63, 0 } } },
+	{ { { 33, 0, 0 }, { 16, 50, 0 } } },
+	{ { { 33, 0, 1 }, { 4, 63, 0 } } },
+	{ { { 33, 0, 2 }, { 16, 51, 0 } } },
+	{ { { 34, 0, 1 }, { 5, 63, 0 } } },
+	{ { { 34, 0, 0 }, { 16, 52, 0 } } },
+	{ { { 34, 0, 1 }, { 6, 63, 0 } } },
+	{ { { 34, 0, 2 }, { 16, 53, 0 } } },
+	{ { { 35, 0, 1 }, { 7, 63, 0 } } },
+	{ { { 35, 0, 0 }, { 16, 54, 0 } } },
+	{ { { 35, 0, 1 }, { 8, 63, 0 } } },
+	{ { { 35, 0, 2 }, { 16, 55, 0 } } },
+	{ { { 36, 0, 1 }, { 9, 63, 0 } } },
+	{ { { 36, 0, 0 }, { 16, 56, 0 } } },
+	{ { { 36, 0, 1 }, { 10, 63, 0 } } },
+	{ { { 36, 0, 2 }, { 16, 57, 0 } } },
+	{ { { 37, 0, 1 }, { 11, 63, 0 } } },
+	{ { { 37, 0, 0 }, { 16, 58, 0 } } },
+	{ { { 37, 0, 1 }, { 12, 63, 0 } } },
+	{ { { 37, 0, 2 }, { 16, 59, 0 } } },
+	{ { { 38, 0, 1 }, { 13, 63, 0 } } },
+	{ { { 38, 0, 0 }, { 16, 60, 0 } } },
+	{ { { 38, 0, 1 }, { 14, 63, 0 } } },
+	{ { { 38, 0, 2 }, { 16, 61, 0 } } },
+	{ { { 39, 0, 1 }, { 15, 63, 0 } } },
+	{ { { 39, 0, 0 }, { 16, 62, 0 } } },
+	{ { { 39, 0, 1 }, { 16, 63, 1 } } },
+	{ { { 39, 0, 2 }, { 16, 63, 0 } } },
+	{ { { 40, 0, 1 }, { 17, 63, 1 } } },
+	{ { { 40, 0, 0 }, { 17, 63, 0 } } },
+	{ { { 40, 0, 1 }, { 18, 63, 1 } } },
+	{ { { 40, 0, 2 }, { 18, 63, 0 } } },
+	{ { { 41, 0, 1 }, { 19, 63, 1 } } },
+	{ { { 41, 0, 0 }, { 19, 63, 0 } } },
+	{ { { 41, 0, 1 }, { 20, 63, 1 } } },
+	{ { { 41, 0, 2 }, { 20, 63, 0 } } },
+	{ { { 42, 0, 1 }, { 21, 63, 1 } } },
+	{ { { 42, 0, 0 }, { 21, 63, 0 } } },
+	{ { { 42, 0, 1 }, { 22, 63, 1 } } },
+	{ { { 42, 0, 2 }, { 22, 63, 0 } } },
+	{ { { 43, 0, 1 }, { 23, 63, 1 } } },
+	{ { { 43, 0, 0 }, { 23, 63, 0 } } },
+	{ { { 43, 0, 1 }, { 24, 63, 1 } } },
+	{ { { 43, 0, 2 }, { 24, 63, 0 } } },
+	{ { { 44, 0, 1 }, { 25, 63, 1 } } },
+	{ { { 44, 0, 0 }, { 25, 63, 0 } } },
+	{ { { 44, 0, 1 }, { 26, 63, 1 } } },
+	{ { { 44, 0, 2 }, { 26, 63, 0 } } },
+	{ { { 45, 0, 1 }, { 27, 63, 1 } } },
+	{ { { 45, 0, 0 }, { 27, 63, 0 } } },
+	{ { { 45, 0, 1 }, { 28, 63, 1 } } },
+	{ { { 45, 0, 2 }, { 28, 63, 0 } } },
+	{ { { 46, 0, 1 }, { 29, 63, 1 } } },
+	{ { { 46, 0, 0 }, { 29, 63, 0 } } },
+	{ { { 46, 0, 1 }, { 30, 63, 1 } } },
+	{ { { 46, 0, 2 }, { 30, 63, 0 } } },
+	{ { { 47, 0, 1 }, { 31, 63, 1 } } },
+	{ { { 47, 0, 0 }, { 31, 63, 0 } } },
+	{ { { 47, 0, 1 }, { 32, 63, 1 } } },
+	{ { { 47, 0, 2 }, { 32, 63, 0 } } },
+	{ { { 48, 0, 2 }, { 33, 63, 1 } } },
+	{ { { 48, 0, 1 }, { 33, 63, 0 } } },
+	{ { { 48, 0, 0 }, { 48, 48, 0 } } },
+	{ { { 48, 0, 1 }, { 34, 63, 0 } } },
+	{ { { 48, 0, 2 }, { 48, 49, 0 } } },
+	{ { { 49, 0, 1 }, { 35, 63, 0 } } },
+	{ { { 49, 0, 0 }, { 48, 50, 0 } } },
+	{ { { 49, 0, 1 }, { 36, 63, 0 } } },
+	{ { { 49, 0, 2 }, { 48, 51, 0 } } },
+	{ { { 50, 0, 1 }, { 37, 63, 0 } } },
+	{ { { 50, 0, 0 }, { 48, 52, 0 } } },
+	{ { { 50, 0, 1 }, { 38, 63, 0 } } },
+	{ { { 50, 0, 2 }, { 48, 53, 0 } } },
+	{ { { 51, 0, 1 }, { 39, 63, 0 } } },
+	{ { { 51, 0, 0 }, { 48, 54, 0 } } },
+	{ { { 51, 0, 1 }, { 40, 63, 0 } } },
+	{ { { 51, 0, 2 }, { 48, 55, 0 } } },
+	{ { { 52, 0, 1 }, { 41, 63, 0 } } },
+	{ { { 52, 0, 0 }, { 48, 56, 0 } } },
+	{ { { 52, 0, 1 }, { 42, 63, 0 } } },
+	{ { { 52, 0, 2 }, { 48, 57, 0 } } },
+	{ { { 53, 0, 1 }, { 43, 63, 0 } } },
+	{ { { 53, 0, 0 }, { 48, 58, 0 } } },
+	{ { { 53, 0, 1 }, { 44, 63, 0 } } },
+	{ { { 53, 0, 2 }, { 48, 59, 0 } } },
+	{ { { 54, 0, 1 }, { 45, 63, 0 } } },
+	{ { { 54, 0, 0 }, { 48, 60, 0 } } },
+	{ { { 54, 0, 1 }, { 46, 63, 0 } } },
+	{ { { 54, 0, 2 }, { 48, 61, 0 } } },
+	{ { { 55, 0, 1 }, { 47, 63, 0 } } },
+	{ { { 55, 0, 0 }, { 48, 62, 0 } } },
+	{ { { 55, 0, 1 }, { 48, 63, 1 } } },
+	{ { { 55, 0, 2 }, { 48, 63, 0 } } },
+	{ { { 56, 0, 1 }, { 49, 63, 1 } } },
+	{ { { 56, 0, 0 }, { 49, 63, 0 } } },
+	{ { { 56, 0, 1 }, { 50, 63, 1 } } },
+	{ { { 56, 0, 2 }, { 50, 63, 0 } } },
+	{ { { 57, 0, 1 }, { 51, 63, 1 } } },
+	{ { { 57, 0, 0 }, { 51, 63, 0 } } },
+	{ { { 57, 0, 1 }, { 52, 63, 1 } } },
+	{ { { 57, 0, 2 }, { 52, 63, 0 } } },
+	{ { { 58, 0, 1 }, { 53, 63, 1 } } },
+	{ { { 58, 0, 0 }, { 53, 63, 0 } } },
+	{ { { 58, 0, 1 }, { 54, 63, 1 } } },
+	{ { { 58, 0, 2 }, { 54, 63, 0 } } },
+	{ { { 59, 0, 1 }, { 55, 63, 1 } } },
+	{ { { 59, 0, 0 }, { 55, 63, 0 } } },
+	{ { { 59, 0, 1 }, { 56, 63, 1 } } },
+	{ { { 59, 0, 2 }, { 56, 63, 0 } } },
+	{ { { 60, 0, 1 }, { 57, 63, 1 } } },
+	{ { { 60, 0, 0 }, { 57, 63, 0 } } },
+	{ { { 60, 0, 1 }, { 58, 63, 1 } } },
+	{ { { 60, 0, 2 }, { 58, 63, 0 } } },
+	{ { { 61, 0, 1 }, { 59, 63, 1 } } },
+	{ { { 61, 0, 0 }, { 59, 63, 0 } } },
+	{ { { 61, 0, 1 }, { 60, 63, 1 } } },
+	{ { { 61, 0, 2 }, { 60, 63, 0 } } },
+	{ { { 62, 0, 1 }, { 61, 63, 1 } } },
+	{ { { 62, 0, 0 }, { 61, 63, 0 } } },
+	{ { { 62, 0, 1 }, { 62, 63, 1 } } },
+	{ { { 62, 0, 2 }, { 62, 63, 0 } } },
+	{ { { 63, 0, 1 }, { 63, 63, 1 } } },
+	{ { { 63, 0, 0 }, { 63, 63, 0 } } }
+};
+
+static SingleColourLookup const lookup_5_4[] = 
+{
+	{ { { 0, 0, 0 }, { 0, 0, 0 } } },
+	{ { { 0, 0, 1 }, { 0, 1, 1 } } },
+	{ { { 0, 0, 2 }, { 0, 1, 0 } } },
+	{ { { 0, 0, 3 }, { 0, 1, 1 } } },
+	{ { { 0, 0, 4 }, { 0, 2, 1 } } },
+	{ { { 1, 0, 3 }, { 0, 2, 0 } } },
+	{ { { 1, 0, 2 }, { 0, 2, 1 } } },
+	{ { { 1, 0, 1 }, { 0, 3, 1 } } },
+	{ { { 1, 0, 0 }, { 0, 3, 0 } } },
+	{ { { 1, 0, 1 }, { 1, 2, 1 } } },
+	{ { { 1, 0, 2 }, { 1, 2, 0 } } },
+	{ { { 1, 0, 3 }, { 0, 4, 0 } } },
+	{ { { 1, 0, 4 }, { 0, 5, 1 } } },
+	{ { { 2, 0, 3 }, { 0, 5, 0 } } },
+	{ { { 2, 0, 2 }, { 0, 5, 1 } } },
+	{ { { 2, 0, 1 }, { 0, 6, 1 } } },
+	{ { { 2, 0, 0 }, { 0, 6, 0 } } },
+	{ { { 2, 0, 1 }, { 2, 3, 1 } } },
+	{ { { 2, 0, 2 }, { 2, 3, 0 } } },
+	{ { { 2, 0, 3 }, { 0, 7, 0 } } },
+	{ { { 2, 0, 4 }, { 1, 6, 1 } } },
+	{ { { 3, 0, 3 }, { 1, 6, 0 } } },
+	{ { { 3, 0, 2 }, { 0, 8, 0 } } },
+	{ { { 3, 0, 1 }, { 0, 9, 1 } } },
+	{ { { 3, 0, 0 }, { 0, 9, 0 } } },
+	{ { { 3, 0, 1 }, { 0, 9, 1 } } },
+	{ { { 3, 0, 2 }, { 0, 10, 1 } } },
+	{ { { 3, 0, 3 }, { 0, 10, 0 } } },
+	{ { { 3, 0, 4 }, { 2, 7, 1 } } },
+	{ { { 4, 0, 4 }, { 2, 7, 0 } } },
+	{ { { 4, 0, 3 }, { 0, 11, 0 } } },
+	{ { { 4, 0, 2 }, { 1, 10, 1 } } },
+	{ { { 4, 0, 1 }, { 1, 10, 0 } } },
+	{ { { 4, 0, 0 }, { 0, 12, 0 } } },
+	{ { { 4, 0, 1 }, { 0, 13, 1 } } },
+	{ { { 4, 0, 2 }, { 0, 13, 0 } } },
+	{ { { 4, 0, 3 }, { 0, 13, 1 } } },
+	{ { { 4, 0, 4 }, { 0, 14, 1 } } },
+	{ { { 5, 0, 3 }, { 0, 14, 0 } } },
+	{ { { 5, 0, 2 }, { 2, 11, 1 } } },
+	{ { { 5, 0, 1 }, { 2, 11, 0 } } },
+	{ { { 5, 0, 0 }, { 0, 15, 0 } } },
+	{ { { 5, 0, 1 }, { 1, 14, 1 } } },
+	{ { { 5, 0, 2 }, { 1, 14, 0 } } },
+	{ { { 5, 0, 3 }, { 0, 16, 0 } } },
+	{ { { 5, 0, 4 }, { 0, 17, 1 } } },
+	{ { { 6, 0, 3 }, { 0, 17, 0 } } },
+	{ { { 6, 0, 2 }, { 0, 17, 1 } } },
+	{ { { 6, 0, 1 }, { 0, 18, 1 } } },
+	{ { { 6, 0, 0 }, { 0, 18, 0 } } },
+	{ { { 6, 0, 1 }, { 2, 15, 1 } } },
+	{ { { 6, 0, 2 }, { 2, 15, 0 } } },
+	{ { { 6, 0, 3 }, { 0, 19, 0 } } },
+	{ { { 6, 0, 4 }, { 1, 18, 1 } } },
+	{ { { 7, 0, 3 }, { 1, 18, 0 } } },
+	{ { { 7, 0, 2 }, { 0, 20, 0 } } },
+	{ { { 7, 0, 1 }, { 0, 21, 1 } } },
+	{ { { 7, 0, 0 }, { 0, 21, 0 } } },
+	{ { { 7, 0, 1 }, { 0, 21, 1 } } },
+	{ { { 7, 0, 2 }, { 0, 22, 1 } } },
+	{ { { 7, 0, 3 }, { 0, 22, 0 } } },
+	{ { { 7, 0, 4 }, { 2, 19, 1 } } },
+	{ { { 8, 0, 4 }, { 2, 19, 0 } } },
+	{ { { 8, 0, 3 }, { 0, 23, 0 } } },
+	{ { { 8, 0, 2 }, { 1, 22, 1 } } },
+	{ { { 8, 0, 1 }, { 1, 22, 0 } } },
+	{ { { 8, 0, 0 }, { 0, 24, 0 } } },
+	{ { { 8, 0, 1 }, { 0, 25, 1 } } },
+	{ { { 8, 0, 2 }, { 0, 25, 0 } } },
+	{ { { 8, 0, 3 }, { 0, 25, 1 } } },
+	{ { { 8, 0, 4 }, { 0, 26, 1 } } },
+	{ { { 9, 0, 3 }, { 0, 26, 0 } } },
+	{ { { 9, 0, 2 }, { 2, 23, 1 } } },
+	{ { { 9, 0, 1 }, { 2, 23, 0 } } },
+	{ { { 9, 0, 0 }, { 0, 27, 0 } } },
+	{ { { 9, 0, 1 }, { 1, 26, 1 } } },
+	{ { { 9, 0, 2 }, { 1, 26, 0 } } },
+	{ { { 9, 0, 3 }, { 0, 28, 0 } } },
+	{ { { 9, 0, 4 }, { 0, 29, 1 } } },
+	{ { { 10, 0, 3 }, { 0, 29, 0 } } },
+	{ { { 10, 0, 2 }, { 0, 29, 1 } } },
+	{ { { 10, 0, 1 }, { 0, 30, 1 } } },
+	{ { { 10, 0, 0 }, { 0, 30, 0 } } },
+	{ { { 10, 0, 1 }, { 2, 27, 1 } } },
+	{ { { 10, 0, 2 }, { 2, 27, 0 } } },
+	{ { { 10, 0, 3 }, { 0, 31, 0 } } },
+	{ { { 10, 0, 4 }, { 1, 30, 1 } } },
+	{ { { 11, 0, 3 }, { 1, 30, 0 } } },
+	{ { { 11, 0, 2 }, { 4, 24, 0 } } },
+	{ { { 11, 0, 1 }, { 1, 31, 1 } } },
+	{ { { 11, 0, 0 }, { 1, 31, 0 } } },
+	{ { { 11, 0, 1 }, { 1, 31, 1 } } },
+	{ { { 11, 0, 2 }, { 2, 30, 1 } } },
+	{ { { 11, 0, 3 }, { 2, 30, 0 } } },
+	{ { { 11, 0, 4 }, { 2, 31, 1 } } },
+	{ { { 12, 0, 4 }, { 2, 31, 0 } } },
+	{ { { 12, 0, 3 }, { 4, 27, 0 } } },
+	{ { { 12, 0, 2 }, { 3, 30, 1 } } },
+	{ { { 12, 0, 1 }, { 3, 30, 0 } } },
+	{ { { 12, 0, 0 }, { 4, 28, 0 } } },
+	{ { { 12, 0, 1 }, { 3, 31, 1 } } },
+	{ { { 12, 0, 2 }, { 3, 31, 0 } } },
+	{ { { 12, 0, 3 }, { 3, 31, 1 } } },
+	{ { { 12, 0, 4 }, { 4, 30, 1 } } },
+	{ { { 13, 0, 3 }, { 4, 30, 0 } } },
+	{ { { 13, 0, 2 }, { 6, 27, 1 } } },
+	{ { { 13, 0, 1 }, { 6, 27, 0 } } },
+	{ { { 13, 0, 0 }, { 4, 31, 0 } } },
+	{ { { 13, 0, 1 }, { 5, 30, 1 } } },
+	{ { { 13, 0, 2 }, { 5, 30, 0 } } },
+	{ { { 13, 0, 3 }, { 8, 24, 0 } } },
+	{ { { 13, 0, 4 }, { 5, 31, 1 } } },
+	{ { { 14, 0, 3 }, { 5, 31, 0 } } },
+	{ { { 14, 0, 2 }, { 5, 31, 1 } } },
+	{ { { 14, 0, 1 }, { 6, 30, 1 } } },
+	{ { { 14, 0, 0 }, { 6, 30, 0 } } },
+	{ { { 14, 0, 1 }, { 6, 31, 1 } } },
+	{ { { 14, 0, 2 }, { 6, 31, 0 } } },
+	{ { { 14, 0, 3 }, { 8, 27, 0 } } },
+	{ { { 14, 0, 4 }, { 7, 30, 1 } } },
+	{ { { 15, 0, 3 }, { 7, 30, 0 } } },
+	{ { { 15, 0, 2 }, { 8, 28, 0 } } },
+	{ { { 15, 0, 1 }, { 7, 31, 1 } } },
+	{ { { 15, 0, 0 }, { 7, 31, 0 } } },
+	{ { { 15, 0, 1 }, { 7, 31, 1 } } },
+	{ { { 15, 0, 2 }, { 8, 30, 1 } } },
+	{ { { 15, 0, 3 }, { 8, 30, 0 } } },
+	{ { { 15, 0, 4 }, { 10, 27, 1 } } },
+	{ { { 16, 0, 4 }, { 10, 27, 0 } } },
+	{ { { 16, 0, 3 }, { 8, 31, 0 } } },
+	{ { { 16, 0, 2 }, { 9, 30, 1 } } },
+	{ { { 16, 0, 1 }, { 9, 30, 0 } } },
+	{ { { 16, 0, 0 }, { 12, 24, 0 } } },
+	{ { { 16, 0, 1 }, { 9, 31, 1 } } },
+	{ { { 16, 0, 2 }, { 9, 31, 0 } } },
+	{ { { 16, 0, 3 }, { 9, 31, 1 } } },
+	{ { { 16, 0, 4 }, { 10, 30, 1 } } },
+	{ { { 17, 0, 3 }, { 10, 30, 0 } } },
+	{ { { 17, 0, 2 }, { 10, 31, 1 } } },
+	{ { { 17, 0, 1 }, { 10, 31, 0 } } },
+	{ { { 17, 0, 0 }, { 12, 27, 0 } } },
+	{ { { 17, 0, 1 }, { 11, 30, 1 } } },
+	{ { { 17, 0, 2 }, { 11, 30, 0 } } },
+	{ { { 17, 0, 3 }, { 12, 28, 0 } } },
+	{ { { 17, 0, 4 }, { 11, 31, 1 } } },
+	{ { { 18, 0, 3 }, { 11, 31, 0 } } },
+	{ { { 18, 0, 2 }, { 11, 31, 1 } } },
+	{ { { 18, 0, 1 }, { 12, 30, 1 } } },
+	{ { { 18, 0, 0 }, { 12, 30, 0 } } },
+	{ { { 18, 0, 1 }, { 14, 27, 1 } } },
+	{ { { 18, 0, 2 }, { 14, 27, 0 } } },
+	{ { { 18, 0, 3 }, { 12, 31, 0 } } },
+	{ { { 18, 0, 4 }, { 13, 30, 1 } } },
+	{ { { 19, 0, 3 }, { 13, 30, 0 } } },
+	{ { { 19, 0, 2 }, { 16, 24, 0 } } },
+	{ { { 19, 0, 1 }, { 13, 31, 1 } } },
+	{ { { 19, 0, 0 }, { 13, 31, 0 } } },
+	{ { { 19, 0, 1 }, { 13, 31, 1 } } },
+	{ { { 19, 0, 2 }, { 14, 30, 1 } } },
+	{ { { 19, 0, 3 }, { 14, 30, 0 } } },
+	{ { { 19, 0, 4 }, { 14, 31, 1 } } },
+	{ { { 20, 0, 4 }, { 14, 31, 0 } } },
+	{ { { 20, 0, 3 }, { 16, 27, 0 } } },
+	{ { { 20, 0, 2 }, { 15, 30, 1 } } },
+	{ { { 20, 0, 1 }, { 15, 30, 0 } } },
+	{ { { 20, 0, 0 }, { 16, 28, 0 } } },
+	{ { { 20, 0, 1 }, { 15, 31, 1 } } },
+	{ { { 20, 0, 2 }, { 15, 31, 0 } } },
+	{ { { 20, 0, 3 }, { 15, 31, 1 } } },
+	{ { { 20, 0, 4 }, { 16, 30, 1 } } },
+	{ { { 21, 0, 3 }, { 16, 30, 0 } } },
+	{ { { 21, 0, 2 }, { 18, 27, 1 } } },
+	{ { { 21, 0, 1 }, { 18, 27, 0 } } },
+	{ { { 21, 0, 0 }, { 16, 31, 0 } } },
+	{ { { 21, 0, 1 }, { 17, 30, 1 } } },
+	{ { { 21, 0, 2 }, { 17, 30, 0 } } },
+	{ { { 21, 0, 3 }, { 20, 24, 0 } } },
+	{ { { 21, 0, 4 }, { 17, 31, 1 } } },
+	{ { { 22, 0, 3 }, { 17, 31, 0 } } },
+	{ { { 22, 0, 2 }, { 17, 31, 1 } } },
+	{ { { 22, 0, 1 }, { 18, 30, 1 } } },
+	{ { { 22, 0, 0 }, { 18, 30, 0 } } },
+	{ { { 22, 0, 1 }, { 18, 31, 1 } } },
+	{ { { 22, 0, 2 }, { 18, 31, 0 } } },
+	{ { { 22, 0, 3 }, { 20, 27, 0 } } },
+	{ { { 22, 0, 4 }, { 19, 30, 1 } } },
+	{ { { 23, 0, 3 }, { 19, 30, 0 } } },
+	{ { { 23, 0, 2 }, { 20, 28, 0 } } },
+	{ { { 23, 0, 1 }, { 19, 31, 1 } } },
+	{ { { 23, 0, 0 }, { 19, 31, 0 } } },
+	{ { { 23, 0, 1 }, { 19, 31, 1 } } },
+	{ { { 23, 0, 2 }, { 20, 30, 1 } } },
+	{ { { 23, 0, 3 }, { 20, 30, 0 } } },
+	{ { { 23, 0, 4 }, { 22, 27, 1 } } },
+	{ { { 24, 0, 4 }, { 22, 27, 0 } } },
+	{ { { 24, 0, 3 }, { 20, 31, 0 } } },
+	{ { { 24, 0, 2 }, { 21, 30, 1 } } },
+	{ { { 24, 0, 1 }, { 21, 30, 0 } } },
+	{ { { 24, 0, 0 }, { 24, 24, 0 } } },
+	{ { { 24, 0, 1 }, { 21, 31, 1 } } },
+	{ { { 24, 0, 2 }, { 21, 31, 0 } } },
+	{ { { 24, 0, 3 }, { 21, 31, 1 } } },
+	{ { { 24, 0, 4 }, { 22, 30, 1 } } },
+	{ { { 25, 0, 3 }, { 22, 30, 0 } } },
+	{ { { 25, 0, 2 }, { 22, 31, 1 } } },
+	{ { { 25, 0, 1 }, { 22, 31, 0 } } },
+	{ { { 25, 0, 0 }, { 24, 27, 0 } } },
+	{ { { 25, 0, 1 }, { 23, 30, 1 } } },
+	{ { { 25, 0, 2 }, { 23, 30, 0 } } },
+	{ { { 25, 0, 3 }, { 24, 28, 0 } } },
+	{ { { 25, 0, 4 }, { 23, 31, 1 } } },
+	{ { { 26, 0, 3 }, { 23, 31, 0 } } },
+	{ { { 26, 0, 2 }, { 23, 31, 1 } } },
+	{ { { 26, 0, 1 }, { 24, 30, 1 } } },
+	{ { { 26, 0, 0 }, { 24, 30, 0 } } },
+	{ { { 26, 0, 1 }, { 26, 27, 1 } } },
+	{ { { 26, 0, 2 }, { 26, 27, 0 } } },
+	{ { { 26, 0, 3 }, { 24, 31, 0 } } },
+	{ { { 26, 0, 4 }, { 25, 30, 1 } } },
+	{ { { 27, 0, 3 }, { 25, 30, 0 } } },
+	{ { { 27, 0, 2 }, { 28, 24, 0 } } },
+	{ { { 27, 0, 1 }, { 25, 31, 1 } } },
+	{ { { 27, 0, 0 }, { 25, 31, 0 } } },
+	{ { { 27, 0, 1 }, { 25, 31, 1 } } },
+	{ { { 27, 0, 2 }, { 26, 30, 1 } } },
+	{ { { 27, 0, 3 }, { 26, 30, 0 } } },
+	{ { { 27, 0, 4 }, { 26, 31, 1 } } },
+	{ { { 28, 0, 4 }, { 26, 31, 0 } } },
+	{ { { 28, 0, 3 }, { 28, 27, 0 } } },
+	{ { { 28, 0, 2 }, { 27, 30, 1 } } },
+	{ { { 28, 0, 1 }, { 27, 30, 0 } } },
+	{ { { 28, 0, 0 }, { 28, 28, 0 } } },
+	{ { { 28, 0, 1 }, { 27, 31, 1 } } },
+	{ { { 28, 0, 2 }, { 27, 31, 0 } } },
+	{ { { 28, 0, 3 }, { 27, 31, 1 } } },
+	{ { { 28, 0, 4 }, { 28, 30, 1 } } },
+	{ { { 29, 0, 3 }, { 28, 30, 0 } } },
+	{ { { 29, 0, 2 }, { 30, 27, 1 } } },
+	{ { { 29, 0, 1 }, { 30, 27, 0 } } },
+	{ { { 29, 0, 0 }, { 28, 31, 0 } } },
+	{ { { 29, 0, 1 }, { 29, 30, 1 } } },
+	{ { { 29, 0, 2 }, { 29, 30, 0 } } },
+	{ { { 29, 0, 3 }, { 29, 30, 1 } } },
+	{ { { 29, 0, 4 }, { 29, 31, 1 } } },
+	{ { { 30, 0, 3 }, { 29, 31, 0 } } },
+	{ { { 30, 0, 2 }, { 29, 31, 1 } } },
+	{ { { 30, 0, 1 }, { 30, 30, 1 } } },
+	{ { { 30, 0, 0 }, { 30, 30, 0 } } },
+	{ { { 30, 0, 1 }, { 30, 31, 1 } } },
+	{ { { 30, 0, 2 }, { 30, 31, 0 } } },
+	{ { { 30, 0, 3 }, { 30, 31, 1 } } },
+	{ { { 30, 0, 4 }, { 31, 30, 1 } } },
+	{ { { 31, 0, 3 }, { 31, 30, 0 } } },
+	{ { { 31, 0, 2 }, { 31, 30, 1 } } },
+	{ { { 31, 0, 1 }, { 31, 31, 1 } } },
+	{ { { 31, 0, 0 }, { 31, 31, 0 } } }
+};
+
+static SingleColourLookup const lookup_6_4[] = 
+{
+	{ { { 0, 0, 0 }, { 0, 0, 0 } } },
+	{ { { 0, 0, 1 }, { 0, 1, 0 } } },
+	{ { { 0, 0, 2 }, { 0, 2, 0 } } },
+	{ { { 1, 0, 1 }, { 0, 3, 1 } } },
+	{ { { 1, 0, 0 }, { 0, 3, 0 } } },
+	{ { { 1, 0, 1 }, { 0, 4, 0 } } },
+	{ { { 1, 0, 2 }, { 0, 5, 0 } } },
+	{ { { 2, 0, 1 }, { 0, 6, 1 } } },
+	{ { { 2, 0, 0 }, { 0, 6, 0 } } },
+	{ { { 2, 0, 1 }, { 0, 7, 0 } } },
+	{ { { 2, 0, 2 }, { 0, 8, 0 } } },
+	{ { { 3, 0, 1 }, { 0, 9, 1 } } },
+	{ { { 3, 0, 0 }, { 0, 9, 0 } } },
+	{ { { 3, 0, 1 }, { 0, 10, 0 } } },
+	{ { { 3, 0, 2 }, { 0, 11, 0 } } },
+	{ { { 4, 0, 1 }, { 0, 12, 1 } } },
+	{ { { 4, 0, 0 }, { 0, 12, 0 } } },
+	{ { { 4, 0, 1 }, { 0, 13, 0 } } },
+	{ { { 4, 0, 2 }, { 0, 14, 0 } } },
+	{ { { 5, 0, 1 }, { 0, 15, 1 } } },
+	{ { { 5, 0, 0 }, { 0, 15, 0 } } },
+	{ { { 5, 0, 1 }, { 0, 16, 0 } } },
+	{ { { 5, 0, 2 }, { 1, 15, 0 } } },
+	{ { { 6, 0, 1 }, { 0, 17, 0 } } },
+	{ { { 6, 0, 0 }, { 0, 18, 0 } } },
+	{ { { 6, 0, 1 }, { 0, 19, 0 } } },
+	{ { { 6, 0, 2 }, { 3, 14, 0 } } },
+	{ { { 7, 0, 1 }, { 0, 20, 0 } } },
+	{ { { 7, 0, 0 }, { 0, 21, 0 } } },
+	{ { { 7, 0, 1 }, { 0, 22, 0 } } },
+	{ { { 7, 0, 2 }, { 4, 15, 0 } } },
+	{ { { 8, 0, 1 }, { 0, 23, 0 } } },
+	{ { { 8, 0, 0 }, { 0, 24, 0 } } },
+	{ { { 8, 0, 1 }, { 0, 25, 0 } } },
+	{ { { 8, 0, 2 }, { 6, 14, 0 } } },
+	{ { { 9, 0, 1 }, { 0, 26, 0 } } },
+	{ { { 9, 0, 0 }, { 0, 27, 0 } } },
+	{ { { 9, 0, 1 }, { 0, 28, 0 } } },
+	{ { { 9, 0, 2 }, { 7, 15, 0 } } },
+	{ { { 10, 0, 1 }, { 0, 29, 0 } } },
+	{ { { 10, 0, 0 }, { 0, 30, 0 } } },
+	{ { { 10, 0, 1 }, { 0, 31, 0 } } },
+	{ { { 10, 0, 2 }, { 9, 14, 0 } } },
+	{ { { 11, 0, 1 }, { 0, 32, 0 } } },
+	{ { { 11, 0, 0 }, { 0, 33, 0 } } },
+	{ { { 11, 0, 1 }, { 2, 30, 0 } } },
+	{ { { 11, 0, 2 }, { 0, 34, 0 } } },
+	{ { { 12, 0, 1 }, { 0, 35, 0 } } },
+	{ { { 12, 0, 0 }, { 0, 36, 0 } } },
+	{ { { 12, 0, 1 }, { 3, 31, 0 } } },
+	{ { { 12, 0, 2 }, { 0, 37, 0 } } },
+	{ { { 13, 0, 1 }, { 0, 38, 0 } } },
+	{ { { 13, 0, 0 }, { 0, 39, 0 } } },
+	{ { { 13, 0, 1 }, { 5, 30, 0 } } },
+	{ { { 13, 0, 2 }, { 0, 40, 0 } } },
+	{ { { 14, 0, 1 }, { 0, 41, 0 } } },
+	{ { { 14, 0, 0 }, { 0, 42, 0 } } },
+	{ { { 14, 0, 1 }, { 6, 31, 0 } } },
+	{ { { 14, 0, 2 }, { 0, 43, 0 } } },
+	{ { { 15, 0, 1 }, { 0, 44, 0 } } },
+	{ { { 15, 0, 0 }, { 0, 45, 0 } } },
+	{ { { 15, 0, 1 }, { 8, 30, 0 } } },
+	{ { { 15, 0, 2 }, { 0, 46, 0 } } },
+	{ { { 16, 0, 2 }, { 0, 47, 0 } } },
+	{ { { 16, 0, 1 }, { 1, 46, 0 } } },
+	{ { { 16, 0, 0 }, { 0, 48, 0 } } },
+	{ { { 16, 0, 1 }, { 0, 49, 0 } } },
+	{ { { 16, 0, 2 }, { 0, 50, 0 } } },
+	{ { { 17, 0, 1 }, { 2, 47, 0 } } },
+	{ { { 17, 0, 0 }, { 0, 51, 0 } } },
+	{ { { 17, 0, 1 }, { 0, 52, 0 } } },
+	{ { { 17, 0, 2 }, { 0, 53, 0 } } },
+	{ { { 18, 0, 1 }, { 4, 46, 0 } } },
+	{ { { 18, 0, 0 }, { 0, 54, 0 } } },
+	{ { { 18, 0, 1 }, { 0, 55, 0 } } },
+	{ { { 18, 0, 2 }, { 0, 56, 0 } } },
+	{ { { 19, 0, 1 }, { 5, 47, 0 } } },
+	{ { { 19, 0, 0 }, { 0, 57, 0 } } },
+	{ { { 19, 0, 1 }, { 0, 58, 0 } } },
+	{ { { 19, 0, 2 }, { 0, 59, 0 } } },
+	{ { { 20, 0, 1 }, { 7, 46, 0 } } },
+	{ { { 20, 0, 0 }, { 0, 60, 0 } } },
+	{ { { 20, 0, 1 }, { 0, 61, 0 } } },
+	{ { { 20, 0, 2 }, { 0, 62, 0 } } },
+	{ { { 21, 0, 1 }, { 8, 47, 0 } } },
+	{ { { 21, 0, 0 }, { 0, 63, 0 } } },
+	{ { { 21, 0, 1 }, { 1, 62, 0 } } },
+	{ { { 21, 0, 2 }, { 1, 63, 0 } } },
+	{ { { 22, 0, 1 }, { 10, 46, 0 } } },
+	{ { { 22, 0, 0 }, { 2, 62, 0 } } },
+	{ { { 22, 0, 1 }, { 2, 63, 0 } } },
+	{ { { 22, 0, 2 }, { 3, 62, 0 } } },
+	{ { { 23, 0, 1 }, { 11, 47, 0 } } },
+	{ { { 23, 0, 0 }, { 3, 63, 0 } } },
+	{ { { 23, 0, 1 }, { 4, 62, 0 } } },
+	{ { { 23, 0, 2 }, { 4, 63, 0 } } },
+	{ { { 24, 0, 1 }, { 13, 46, 0 } } },
+	{ { { 24, 0, 0 }, { 5, 62, 0 } } },
+	{ { { 24, 0, 1 }, { 5, 63, 0 } } },
+	{ { { 24, 0, 2 }, { 6, 62, 0 } } },
+	{ { { 25, 0, 1 }, { 14, 47, 0 } } },
+	{ { { 25, 0, 0 }, { 6, 63, 0 } } },
+	{ { { 25, 0, 1 }, { 7, 62, 0 } } },
+	{ { { 25, 0, 2 }, { 7, 63, 0 } } },
+	{ { { 26, 0, 1 }, { 16, 45, 0 } } },
+	{ { { 26, 0, 0 }, { 8, 62, 0 } } },
+	{ { { 26, 0, 1 }, { 8, 63, 0 } } },
+	{ { { 26, 0, 2 }, { 9, 62, 0 } } },
+	{ { { 27, 0, 1 }, { 16, 48, 0 } } },
+	{ { { 27, 0, 0 }, { 9, 63, 0 } } },
+	{ { { 27, 0, 1 }, { 10, 62, 0 } } },
+	{ { { 27, 0, 2 }, { 10, 63, 0 } } },
+	{ { { 28, 0, 1 }, { 16, 51, 0 } } },
+	{ { { 28, 0, 0 }, { 11, 62, 0 } } },
+	{ { { 28, 0, 1 }, { 11, 63, 0 } } },
+	{ { { 28, 0, 2 }, { 12, 62, 0 } } },
+	{ { { 29, 0, 1 }, { 16, 54, 0 } } },
+	{ { { 29, 0, 0 }, { 12, 63, 0 } } },
+	{ { { 29, 0, 1 }, { 13, 62, 0 } } },
+	{ { { 29, 0, 2 }, { 13, 63, 0 } } },
+	{ { { 30, 0, 1 }, { 16, 57, 0 } } },
+	{ { { 30, 0, 0 }, { 14, 62, 0 } } },
+	{ { { 30, 0, 1 }, { 14, 63, 0 } } },
+	{ { { 30, 0, 2 }, { 15, 62, 0 } } },
+	{ { { 31, 0, 1 }, { 16, 60, 0 } } },
+	{ { { 31, 0, 0 }, { 15, 63, 0 } } },
+	{ { { 31, 0, 1 }, { 24, 46, 0 } } },
+	{ { { 31, 0, 2 }, { 16, 62, 0 } } },
+	{ { { 32, 0, 2 }, { 16, 63, 0 } } },
+	{ { { 32, 0, 1 }, { 17, 62, 0 } } },
+	{ { { 32, 0, 0 }, { 25, 47, 0 } } },
+	{ { { 32, 0, 1 }, { 17, 63, 0 } } },
+	{ { { 32, 0, 2 }, { 18, 62, 0 } } },
+	{ { { 33, 0, 1 }, { 18, 63, 0 } } },
+	{ { { 33, 0, 0 }, { 27, 46, 0 } } },
+	{ { { 33, 0, 1 }, { 19, 62, 0 } } },
+	{ { { 33, 0, 2 }, { 19, 63, 0 } } },
+	{ { { 34, 0, 1 }, { 20, 62, 0 } } },
+	{ { { 34, 0, 0 }, { 28, 47, 0 } } },
+	{ { { 34, 0, 1 }, { 20, 63, 0 } } },
+	{ { { 34, 0, 2 }, { 21, 62, 0 } } },
+	{ { { 35, 0, 1 }, { 21, 63, 0 } } },
+	{ { { 35, 0, 0 }, { 30, 46, 0 } } },
+	{ { { 35, 0, 1 }, { 22, 62, 0 } } },
+	{ { { 35, 0, 2 }, { 22, 63, 0 } } },
+	{ { { 36, 0, 1 }, { 23, 62, 0 } } },
+	{ { { 36, 0, 0 }, { 31, 47, 0 } } },
+	{ { { 36, 0, 1 }, { 23, 63, 0 } } },
+	{ { { 36, 0, 2 }, { 24, 62, 0 } } },
+	{ { { 37, 0, 1 }, { 24, 63, 0 } } },
+	{ { { 37, 0, 0 }, { 32, 47, 0 } } },
+	{ { { 37, 0, 1 }, { 25, 62, 0 } } },
+	{ { { 37, 0, 2 }, { 25, 63, 0 } } },
+	{ { { 38, 0, 1 }, { 26, 62, 0 } } },
+	{ { { 38, 0, 0 }, { 32, 50, 0 } } },
+	{ { { 38, 0, 1 }, { 26, 63, 0 } } },
+	{ { { 38, 0, 2 }, { 27, 62, 0 } } },
+	{ { { 39, 0, 1 }, { 27, 63, 0 } } },
+	{ { { 39, 0, 0 }, { 32, 53, 0 } } },
+	{ { { 39, 0, 1 }, { 28, 62, 0 } } },
+	{ { { 39, 0, 2 }, { 28, 63, 0 } } },
+	{ { { 40, 0, 1 }, { 29, 62, 0 } } },
+	{ { { 40, 0, 0 }, { 32, 56, 0 } } },
+	{ { { 40, 0, 1 }, { 29, 63, 0 } } },
+	{ { { 40, 0, 2 }, { 30, 62, 0 } } },
+	{ { { 41, 0, 1 }, { 30, 63, 0 } } },
+	{ { { 41, 0, 0 }, { 32, 59, 0 } } },
+	{ { { 41, 0, 1 }, { 31, 62, 0 } } },
+	{ { { 41, 0, 2 }, { 31, 63, 0 } } },
+	{ { { 42, 0, 1 }, { 32, 61, 0 } } },
+	{ { { 42, 0, 0 }, { 32, 62, 0 } } },
+	{ { { 42, 0, 1 }, { 32, 63, 0 } } },
+	{ { { 42, 0, 2 }, { 41, 46, 0 } } },
+	{ { { 43, 0, 1 }, { 33, 62, 0 } } },
+	{ { { 43, 0, 0 }, { 33, 63, 0 } } },
+	{ { { 43, 0, 1 }, { 34, 62, 0 } } },
+	{ { { 43, 0, 2 }, { 42, 47, 0 } } },
+	{ { { 44, 0, 1 }, { 34, 63, 0 } } },
+	{ { { 44, 0, 0 }, { 35, 62, 0 } } },
+	{ { { 44, 0, 1 }, { 35, 63, 0 } } },
+	{ { { 44, 0, 2 }, { 44, 46, 0 } } },
+	{ { { 45, 0, 1 }, { 36, 62, 0 } } },
+	{ { { 45, 0, 0 }, { 36, 63, 0 } } },
+	{ { { 45, 0, 1 }, { 37, 62, 0 } } },
+	{ { { 45, 0, 2 }, { 45, 47, 0 } } },
+	{ { { 46, 0, 1 }, { 37, 63, 0 } } },
+	{ { { 46, 0, 0 }, { 38, 62, 0 } } },
+	{ { { 46, 0, 1 }, { 38, 63, 0 } } },
+	{ { { 46, 0, 2 }, { 47, 46, 0 } } },
+	{ { { 47, 0, 1 }, { 39, 62, 0 } } },
+	{ { { 47, 0, 0 }, { 39, 63, 0 } } },
+	{ { { 47, 0, 1 }, { 40, 62, 0 } } },
+	{ { { 47, 0, 2 }, { 48, 46, 0 } } },
+	{ { { 48, 0, 2 }, { 40, 63, 0 } } },
+	{ { { 48, 0, 1 }, { 41, 62, 0 } } },
+	{ { { 48, 0, 0 }, { 41, 63, 0 } } },
+	{ { { 48, 0, 1 }, { 48, 49, 0 } } },
+	{ { { 48, 0, 2 }, { 42, 62, 0 } } },
+	{ { { 49, 0, 1 }, { 42, 63, 0 } } },
+	{ { { 49, 0, 0 }, { 43, 62, 0 } } },
+	{ { { 49, 0, 1 }, { 48, 52, 0 } } },
+	{ { { 49, 0, 2 }, { 43, 63, 0 } } },
+	{ { { 50, 0, 1 }, { 44, 62, 0 } } },
+	{ { { 50, 0, 0 }, { 44, 63, 0 } } },
+	{ { { 50, 0, 1 }, { 48, 55, 0 } } },
+	{ { { 50, 0, 2 }, { 45, 62, 0 } } },
+	{ { { 51, 0, 1 }, { 45, 63, 0 } } },
+	{ { { 51, 0, 0 }, { 46, 62, 0 } } },
+	{ { { 51, 0, 1 }, { 48, 58, 0 } } },
+	{ { { 51, 0, 2 }, { 46, 63, 0 } } },
+	{ { { 52, 0, 1 }, { 47, 62, 0 } } },
+	{ { { 52, 0, 0 }, { 47, 63, 0 } } },
+	{ { { 52, 0, 1 }, { 48, 61, 0 } } },
+	{ { { 52, 0, 2 }, { 48, 62, 0 } } },
+	{ { { 53, 0, 1 }, { 56, 47, 0 } } },
+	{ { { 53, 0, 0 }, { 48, 63, 0 } } },
+	{ { { 53, 0, 1 }, { 49, 62, 0 } } },
+	{ { { 53, 0, 2 }, { 49, 63, 0 } } },
+	{ { { 54, 0, 1 }, { 58, 46, 0 } } },
+	{ { { 54, 0, 0 }, { 50, 62, 0 } } },
+	{ { { 54, 0, 1 }, { 50, 63, 0 } } },
+	{ { { 54, 0, 2 }, { 51, 62, 0 } } },
+	{ { { 55, 0, 1 }, { 59, 47, 0 } } },
+	{ { { 55, 0, 0 }, { 51, 63, 0 } } },
+	{ { { 55, 0, 1 }, { 52, 62, 0 } } },
+	{ { { 55, 0, 2 }, { 52, 63, 0 } } },
+	{ { { 56, 0, 1 }, { 61, 46, 0 } } },
+	{ { { 56, 0, 0 }, { 53, 62, 0 } } },
+	{ { { 56, 0, 1 }, { 53, 63, 0 } } },
+	{ { { 56, 0, 2 }, { 54, 62, 0 } } },
+	{ { { 57, 0, 1 }, { 62, 47, 0 } } },
+	{ { { 57, 0, 0 }, { 54, 63, 0 } } },
+	{ { { 57, 0, 1 }, { 55, 62, 0 } } },
+	{ { { 57, 0, 2 }, { 55, 63, 0 } } },
+	{ { { 58, 0, 1 }, { 56, 62, 1 } } },
+	{ { { 58, 0, 0 }, { 56, 62, 0 } } },
+	{ { { 58, 0, 1 }, { 56, 63, 0 } } },
+	{ { { 58, 0, 2 }, { 57, 62, 0 } } },
+	{ { { 59, 0, 1 }, { 57, 63, 1 } } },
+	{ { { 59, 0, 0 }, { 57, 63, 0 } } },
+	{ { { 59, 0, 1 }, { 58, 62, 0 } } },
+	{ { { 59, 0, 2 }, { 58, 63, 0 } } },
+	{ { { 60, 0, 1 }, { 59, 62, 1 } } },
+	{ { { 60, 0, 0 }, { 59, 62, 0 } } },
+	{ { { 60, 0, 1 }, { 59, 63, 0 } } },
+	{ { { 60, 0, 2 }, { 60, 62, 0 } } },
+	{ { { 61, 0, 1 }, { 60, 63, 1 } } },
+	{ { { 61, 0, 0 }, { 60, 63, 0 } } },
+	{ { { 61, 0, 1 }, { 61, 62, 0 } } },
+	{ { { 61, 0, 2 }, { 61, 63, 0 } } },
+	{ { { 62, 0, 1 }, { 62, 62, 1 } } },
+	{ { { 62, 0, 0 }, { 62, 62, 0 } } },
+	{ { { 62, 0, 1 }, { 62, 63, 0 } } },
+	{ { { 62, 0, 2 }, { 63, 62, 0 } } },
+	{ { { 63, 0, 1 }, { 63, 63, 1 } } },
+	{ { { 63, 0, 0 }, { 63, 63, 0 } } }
+};

+ 260 - 0
Source/ThirdParty/libsquish/squish.cpp

@@ -0,0 +1,260 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#include "squish.h"
+#include "colourset.h"
+#include "maths.h"
+#include "rangefit.h"
+#include "clusterfit.h"
+#include "colourblock.h"
+#include "alpha.h"
+#include "singlecolourfit.h"
+
+namespace squish {
+
+static int FixFlags( int flags )
+{
+	// grab the flag bits
+	int method = flags & ( kDxt1 | kDxt3 | kDxt5 | kBc4 | kBc5 );
+	int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit );
+	int extra = flags & kWeightColourByAlpha;
+	
+	// set defaults
+	if ( method != kDxt3
+	&&   method != kDxt5
+	&&   method != kBc4
+	&&   method != kBc5 )
+	{
+		method = kDxt1;
+	}
+	if( fit != kColourRangeFit && fit != kColourIterativeClusterFit )
+		fit = kColourClusterFit;
+		
+	// done
+	return method | fit | extra;
+}
+
+void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric )
+{
+	// fix any bad flags
+	flags = FixFlags( flags );
+
+	if ( ( flags & ( kBc4 | kBc5 ) ) != 0 )
+	{
+		u8 alpha[16*4];
+		for( int i = 0; i < 16; ++i )
+		{
+			alpha[i*4 + 3] = rgba[i*4 + 0]; // copy R to A
+		}
+
+		u8* rBlock = reinterpret_cast< u8* >( block );
+		CompressAlphaDxt5( alpha, mask, rBlock );
+
+		if ( ( flags & ( kBc5 ) ) != 0 )
+		{
+			for( int i = 0; i < 16; ++i )
+			{
+				alpha[i*4 + 3] = rgba[i*4 + 1]; // copy G to A
+			}
+
+			u8* gBlock = reinterpret_cast< u8* >( block ) + 8;
+			CompressAlphaDxt5( alpha, mask, gBlock );
+		}
+
+		return;
+	}
+
+	// get the block locations
+	void* colourBlock = block;
+	void* alphaBlock = block;
+	if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
+		colourBlock = reinterpret_cast< u8* >( block ) + 8;
+
+	// create the minimal point set
+	ColourSet colours( rgba, mask, flags );
+	
+	// check the compression type and compress colour
+	if( colours.GetCount() == 1 )
+	{
+		// always do a single colour fit
+		SingleColourFit fit( &colours, flags );
+		fit.Compress( colourBlock );
+	}
+	else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 )
+	{
+		// do a range fit
+		RangeFit fit( &colours, flags, metric );
+		fit.Compress( colourBlock );
+	}
+	else
+	{
+		// default to a cluster fit (could be iterative or not)
+		ClusterFit fit( &colours, flags, metric );
+		fit.Compress( colourBlock );
+	}
+	
+	// compress alpha separately if necessary
+	if( ( flags & kDxt3 ) != 0 )
+		CompressAlphaDxt3( rgba, mask, alphaBlock );
+	else if( ( flags & kDxt5 ) != 0 )
+		CompressAlphaDxt5( rgba, mask, alphaBlock );
+}
+
+void Decompress( u8* rgba, void const* block, int flags )
+{
+	// fix any bad flags
+	flags = FixFlags( flags );
+
+	// get the block locations
+	void const* colourBlock = block;
+	void const* alphaBock = block;
+	if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
+		colourBlock = reinterpret_cast< u8 const* >( block ) + 8;
+
+	// decompress colour
+	DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
+
+	// decompress alpha separately if necessary
+	if( ( flags & kDxt3 ) != 0 )
+		DecompressAlphaDxt3( rgba, alphaBock );
+	else if( ( flags & kDxt5 ) != 0 )
+		DecompressAlphaDxt5( rgba, alphaBock );
+}
+
+int GetStorageRequirements( int width, int height, int flags )
+{
+	// fix any bad flags
+	flags = FixFlags( flags );
+	
+	// compute the storage requirements
+	int blockcount = ( ( width + 3 )/4 ) * ( ( height + 3 )/4 );
+	int blocksize = ( ( flags & ( kDxt1 | kBc4 ) ) != 0 ) ? 8 : 16;
+	return blockcount*blocksize;
+}
+
+void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric )
+{
+	// fix any bad flags
+	flags = FixFlags( flags );
+
+	// initialise the block output
+	u8* targetBlock = reinterpret_cast< u8* >( blocks );
+	int bytesPerBlock = ( ( flags & ( kDxt1 | kBc4 ) ) != 0 ) ? 8 : 16;
+
+	// loop over blocks
+	for( int y = 0; y < height; y += 4 )
+	{
+		for( int x = 0; x < width; x += 4 )
+		{
+			// build the 4x4 block of pixels
+			u8 sourceRgba[16*4];
+			u8* targetPixel = sourceRgba;
+			int mask = 0;
+			for( int py = 0; py < 4; ++py )
+			{
+				for( int px = 0; px < 4; ++px )
+				{
+					// get the source pixel in the image
+					int sx = x + px;
+					int sy = y + py;
+					
+					// enable if we're in the image
+					if( sx < width && sy < height )
+					{
+						// copy the rgba value
+						u8 const* sourcePixel = rgba + 4*( width*sy + sx );
+						for( int i = 0; i < 4; ++i )
+							*targetPixel++ = *sourcePixel++;
+							
+						// enable this pixel
+						mask |= ( 1 << ( 4*py + px ) );
+					}
+					else
+					{
+						// skip this pixel as its outside the image
+						targetPixel += 4;
+					}
+				}
+			}
+			
+			// compress it into the output
+			CompressMasked( sourceRgba, mask, targetBlock, flags, metric );
+			
+			// advance
+			targetBlock += bytesPerBlock;
+		}
+	}
+}
+
+void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags )
+{
+	// fix any bad flags
+	flags = FixFlags( flags );
+
+	// initialise the block input
+	u8 const* sourceBlock = reinterpret_cast< u8 const* >( blocks );
+	int bytesPerBlock = ( ( flags & ( kDxt1 | kBc4 ) ) != 0 ) ? 8 : 16;
+
+	// loop over blocks
+	for( int y = 0; y < height; y += 4 )
+	{
+		for( int x = 0; x < width; x += 4 )
+		{
+			// decompress the block
+			u8 targetRgba[4*16];
+			Decompress( targetRgba, sourceBlock, flags );
+			
+			// write the decompressed pixels to the correct image locations
+			u8 const* sourcePixel = targetRgba;
+			for( int py = 0; py < 4; ++py )
+			{
+				for( int px = 0; px < 4; ++px )
+				{
+					// get the target location
+					int sx = x + px;
+					int sy = y + py;
+					if( sx < width && sy < height )
+					{
+						u8* targetPixel = rgba + 4*( width*sy + sx );
+						
+						// copy the rgba value
+						for( int i = 0; i < 4; ++i )
+							*targetPixel++ = *sourcePixel++;
+					}
+					else
+					{
+						// skip this pixel as its outside the image
+						sourcePixel += 4;
+					}
+				}
+			}
+			
+			// advance
+			sourceBlock += bytesPerBlock;
+		}
+	}
+}
+
+} // namespace squish

+ 269 - 0
Source/ThirdParty/libsquish/squish.h

@@ -0,0 +1,269 @@
+/* -----------------------------------------------------------------------------
+
+	Copyright (c) 2006 Simon Brown                          [email protected]
+
+	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.
+	
+   -------------------------------------------------------------------------- */
+   
+#ifndef SQUISH_H
+#define SQUISH_H
+
+//! All squish API functions live in this namespace.
+namespace squish {
+
+// -----------------------------------------------------------------------------
+
+//! Typedef a quantity that is a single unsigned byte.
+typedef unsigned char u8;
+
+// -----------------------------------------------------------------------------
+
+enum
+{
+	//! Use DXT1 compression.
+	kDxt1 = ( 1 << 0 ),
+
+	//! Use DXT3 compression.
+	kDxt3 = ( 1 << 1 ),
+
+	//! Use DXT5 compression.
+	kDxt5 = ( 1 << 2 ),
+
+	//! Use BC4 compression.
+	kBc4 = ( 1 << 3 ),
+
+	//! Use BC5 compression.
+	kBc5 = ( 1 << 4 ),
+
+	//! Use a slow but high quality colour compressor (the default).
+	kColourClusterFit = ( 1 << 5 ),
+
+	//! Use a fast but low quality colour compressor.
+	kColourRangeFit	= ( 1 << 6 ),
+
+	//! Weight the colour by alpha during cluster fit (disabled by default).
+	kWeightColourByAlpha = ( 1 << 7 ),
+
+	//! Use a very slow but very high quality colour compressor.
+	kColourIterativeClusterFit = ( 1 << 8 ),
+};
+
+// -----------------------------------------------------------------------------
+
+/*! @brief Compresses a 4x4 block of pixels.
+
+	@param rgba		The rgba values of the 16 source pixels.
+	@param mask		The valid pixel mask.
+	@param block	Storage for the compressed DXT block.
+	@param flags	Compression flags.
+	@param metric	An optional perceptual metric.
+	
+	The source pixels should be presented as a contiguous array of 16 rgba
+	values, with each component as 1 byte each. In memory this should be:
+	
+		{ r1, g1, b1, a1, .... , r16, g16, b16, a16 }
+		
+	The mask parameter enables only certain pixels within the block. The lowest
+	bit enables the first pixel and so on up to the 16th bit. Bits beyond the
+	16th bit are ignored. Pixels that are not enabled are allowed to take
+	arbitrary colours in the output block. An example of how this can be used
+	is in the CompressImage function to disable pixels outside the bounds of
+	the image when the width or height is not divisible by 4.
+	
+	The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, 
+	however, DXT1 will be used by default if none is specified. When using DXT1 
+	compression, 8 bytes of storage are required for the compressed DXT block. 
+	DXT3 and DXT5 compression require 16 bytes of storage per block.
+	
+	The flags parameter can also specify a preferred colour compressor to use 
+	when fitting the RGB components of the data. Possible colour compressors 
+	are: kColourClusterFit (the default), kColourRangeFit (very fast, low 
+	quality) or kColourIterativeClusterFit (slowest, best quality).
+		
+	When using kColourClusterFit or kColourIterativeClusterFit, an additional 
+	flag can be specified to weight the importance of each pixel by its alpha 
+	value. For images that are rendered using alpha blending, this can 
+	significantly increase the perceived quality.
+	
+	The metric parameter can be used to weight the relative importance of each
+	colour channel, or pass NULL to use the default uniform weight of 
+	{ 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that 
+	allowed either uniform or "perceptual" weights with the fixed values
+	{ 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a 
+	contiguous array of 3 floats.
+*/
+void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric = 0 );
+
+// -----------------------------------------------------------------------------
+
+/*! @brief Compresses a 4x4 block of pixels.
+
+	@param rgba		The rgba values of the 16 source pixels.
+	@param block	Storage for the compressed DXT block.
+	@param flags	Compression flags.
+	@param metric	An optional perceptual metric.
+	
+	The source pixels should be presented as a contiguous array of 16 rgba
+	values, with each component as 1 byte each. In memory this should be:
+	
+		{ r1, g1, b1, a1, .... , r16, g16, b16, a16 }
+	
+	The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, 
+	however, DXT1 will be used by default if none is specified. When using DXT1 
+	compression, 8 bytes of storage are required for the compressed DXT block. 
+	DXT3 and DXT5 compression require 16 bytes of storage per block.
+	
+	The flags parameter can also specify a preferred colour compressor to use 
+	when fitting the RGB components of the data. Possible colour compressors 
+	are: kColourClusterFit (the default), kColourRangeFit (very fast, low 
+	quality) or kColourIterativeClusterFit (slowest, best quality).
+		
+	When using kColourClusterFit or kColourIterativeClusterFit, an additional 
+	flag can be specified to weight the importance of each pixel by its alpha 
+	value. For images that are rendered using alpha blending, this can 
+	significantly increase the perceived quality.
+	
+	The metric parameter can be used to weight the relative importance of each
+	colour channel, or pass NULL to use the default uniform weight of 
+	{ 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that 
+	allowed either uniform or "perceptual" weights with the fixed values
+	{ 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a 
+	contiguous array of 3 floats.
+	
+	This method is an inline that calls CompressMasked with a mask of 0xffff, 
+	provided for compatibility with older versions of squish.
+*/
+inline void Compress( u8 const* rgba, void* block, int flags, float* metric = 0 )
+{
+	CompressMasked( rgba, 0xffff, block, flags, metric );
+}
+
+// -----------------------------------------------------------------------------
+
+/*! @brief Decompresses a 4x4 block of pixels.
+
+	@param rgba		Storage for the 16 decompressed pixels.
+	@param block	The compressed DXT block.
+	@param flags	Compression flags.
+
+	The decompressed pixels will be written as a contiguous array of 16 rgba
+	values, with each component as 1 byte each. In memory this is:
+	
+		{ r1, g1, b1, a1, .... , r16, g16, b16, a16 }
+	
+	The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, 
+	however, DXT1 will be used by default if none is specified. All other flags 
+	are ignored.
+*/
+void Decompress( u8* rgba, void const* block, int flags );
+
+// -----------------------------------------------------------------------------
+
+/*! @brief Computes the amount of compressed storage required.
+
+	@param width	The width of the image.
+	@param height	The height of the image.
+	@param flags	Compression flags.
+	
+	The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, 
+	however, DXT1 will be used by default if none is specified. All other flags 
+	are ignored.
+	
+	Most DXT images will be a multiple of 4 in each dimension, but this 
+	function supports arbitrary size images by allowing the outer blocks to
+	be only partially used.
+*/
+int GetStorageRequirements( int width, int height, int flags );
+
+// -----------------------------------------------------------------------------
+
+/*! @brief Compresses an image in memory.
+
+	@param rgba		The pixels of the source.
+	@param width	The width of the source image.
+	@param height	The height of the source image.
+	@param blocks	Storage for the compressed output.
+	@param flags	Compression flags.
+	@param metric	An optional perceptual metric.
+	
+	The source pixels should be presented as a contiguous array of width*height
+	rgba values, with each component as 1 byte each. In memory this should be:
+	
+		{ r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height
+		
+	The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, 
+	however, DXT1 will be used by default if none is specified. When using DXT1 
+	compression, 8 bytes of storage are required for each compressed DXT block. 
+	DXT3 and DXT5 compression require 16 bytes of storage per block.
+	
+	The flags parameter can also specify a preferred colour compressor to use 
+	when fitting the RGB components of the data. Possible colour compressors 
+	are: kColourClusterFit (the default), kColourRangeFit (very fast, low 
+	quality) or kColourIterativeClusterFit (slowest, best quality).
+		
+	When using kColourClusterFit or kColourIterativeClusterFit, an additional 
+	flag can be specified to weight the importance of each pixel by its alpha 
+	value. For images that are rendered using alpha blending, this can 
+	significantly increase the perceived quality.
+	
+	The metric parameter can be used to weight the relative importance of each
+	colour channel, or pass NULL to use the default uniform weight of 
+	{ 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that 
+	allowed either uniform or "perceptual" weights with the fixed values
+	{ 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a 
+	contiguous array of 3 floats.
+	
+	Internally this function calls squish::CompressMasked for each block, which 
+	allows for pixels outside the image to take arbitrary values. The function 
+	squish::GetStorageRequirements can be called to compute the amount of memory
+	to allocate for the compressed output.
+*/
+void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric = 0 );
+
+// -----------------------------------------------------------------------------
+
+/*! @brief Decompresses an image in memory.
+
+	@param rgba		Storage for the decompressed pixels.
+	@param width	The width of the source image.
+	@param height	The height of the source image.
+	@param blocks	The compressed DXT blocks.
+	@param flags	Compression flags.
+	
+	The decompressed pixels will be written as a contiguous array of width*height
+	16 rgba values, with each component as 1 byte each. In memory this is:
+	
+		{ r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height
+		
+	The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, 
+	however, DXT1 will be used by default if none is specified. All other flags 
+	are ignored.
+
+	Internally this function calls squish::Decompress for each block.
+*/
+void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags );
+
+// -----------------------------------------------------------------------------
+
+} // namespace squish
+
+#endif // ndef SQUISH_H
+

+ 21 - 0
Source/ToolCore/Assets/AssetDatabase.cpp

@@ -30,6 +30,7 @@
 #include <Atomic/Resource/ResourceEvents.h>
 #include <Atomic/Resource/ResourceCache.h>
 
+#include "../Import/ImportConfig.h"
 #include "../ToolEvents.h"
 #include "../ToolSystem.h"
 #include "../Project/Project.h"
@@ -102,6 +103,24 @@ void AssetDatabase::RegisterGUID(const String& guid)
     usedGUID_.Push(guid);
 }
 
+void AssetDatabase::ReadImportConfig()
+{
+    ImportConfig::Clear();
+
+    ToolSystem* tsystem = GetSubsystem<ToolSystem>();
+    Project* project = tsystem->GetProject();
+
+    String projectPath = project->GetProjectPath();
+
+    String filename = projectPath + "Settings/Import.json";
+
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+    if (!fileSystem->FileExists(filename))
+        return;
+
+    ImportConfig::LoadFromFile(context_, filename);
+}
+
 void AssetDatabase::Import(const String& path)
 {
     FileSystem* fs = GetSubsystem<FileSystem>();
@@ -434,6 +453,8 @@ void AssetDatabase::HandleProjectLoaded(StringHash eventType, VariantMap& eventD
 {
     project_ = GetSubsystem<ToolSystem>()->GetProject();
 
+    ReadImportConfig();
+
     FileSystem* fs = GetSubsystem<FileSystem>();
 
     if (!fs->DirExists(GetCachePath()))

+ 1 - 0
Source/ToolCore/Assets/AssetDatabase.h

@@ -79,6 +79,7 @@ private:
 
     void PruneOrphanedDotAssetFiles();
 
+    void ReadImportConfig();
     void Import(const String& path);
 
     bool ImportDirtyAssets();

+ 16 - 9
Source/ToolCore/Assets/PrefabImporter.cpp

@@ -152,15 +152,7 @@ void PrefabImporter::HandlePrefabSave(StringHash eventType, VariantMap& eventDat
     FileSystem* fs = GetSubsystem<FileSystem>();
     fs->Copy(asset_->GetPath(), asset_->GetCachePath());
 
-    // reload it immediately so it is ready for use
-    // TODO: The resource cache is reloading after this reload due to catching the file cache
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    XMLFile* xmlfile = cache->GetResource<XMLFile>(asset_->GetGUID());
-    cache->ReloadResource(xmlfile);
-
-    VariantMap changedData;
-    changedData[PrefabChanged::P_GUID] = asset_->GetGUID();
-    SendEvent(E_PREFABCHANGED, changedData);
+    OnPrefabFileChanged();
 
 }
 
@@ -170,9 +162,24 @@ bool PrefabImporter::Import()
 
     fs->Copy(asset_->GetPath(), asset_->GetCachePath());
 
+    OnPrefabFileChanged();
+
     return true;
 }
 
+void PrefabImporter::OnPrefabFileChanged()
+{
+    // reload it immediately so it is ready for use
+    // TODO: The resource cache is reloading after this reload due to catching the file cache
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    XMLFile* xmlfile = cache->GetResource<XMLFile>(asset_->GetGUID());
+    cache->ReloadResource(xmlfile);
+
+    VariantMap changedData;
+    changedData[PrefabChanged::P_GUID] = asset_->GetGUID();
+    SendEvent(E_PREFABCHANGED, changedData);
+}
+
 bool PrefabImporter::LoadSettingsInternal(JSONValue& jsonRoot)
 {
     if (!AssetImporter::LoadSettingsInternal(jsonRoot))

+ 3 - 0
Source/ToolCore/Assets/PrefabImporter.h

@@ -55,6 +55,9 @@ protected:
     virtual bool LoadSettingsInternal(JSONValue& jsonRoot);
     virtual bool SaveSettingsInternal(JSONValue& jsonRoot);
 
+    /// Handle notifying any objects that might need to update after the prefab file changes, such as ResourceCache or any scene components
+    virtual void OnPrefabFileChanged();
+
 private:
 
     void HandlePrefabSave(StringHash eventType, VariantMap& eventData);

+ 36 - 5
Source/ToolCore/Assets/TextureImporter.cpp

@@ -24,6 +24,9 @@
 #include <Atomic/Resource/Image.h>
 #include <Atomic/Atomic2D/Sprite2D.h>
 #include <Atomic/Atomic2D/StaticSprite2D.h>
+#include <Atomic/IO/FileSystem.h>
+
+#include <ToolCore/Import/ImportConfig.h>
 
 #include "Asset.h"
 #include "AssetDatabase.h"
@@ -32,9 +35,10 @@
 namespace ToolCore
 {
 
-TextureImporter::TextureImporter(Context* context, Asset *asset) : AssetImporter(context, asset)
+TextureImporter::TextureImporter(Context* context, Asset *asset) : AssetImporter(context, asset),
+compressTextures_(false)
 {
-
+    ApplyProjectImportConfig();
 }
 
 TextureImporter::~TextureImporter()
@@ -50,18 +54,29 @@ void TextureImporter::SetDefaults()
 bool TextureImporter::Import()
 {
     AssetDatabase* db = GetSubsystem<AssetDatabase>();
-
     ResourceCache* cache = GetSubsystem<ResourceCache>();
+    String cachePath = db->GetCachePath();
+
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+    String compressedPath = cachePath + "DDS/" + asset_->GetRelativePath() + ".dds";
+    if (fileSystem->FileExists(compressedPath))
+        fileSystem->Delete(compressedPath);
+
     SharedPtr<Image> image = cache->GetTempResource<Image>(asset_->GetPath());
 
     if (image.Null())
         return false;
 
+    if (compressTextures_ &&
+        !image->IsCompressed())
+    {
+        fileSystem->CreateDirs(cachePath, "DDS/" + Atomic::GetPath(asset_->GetRelativePath()));
+        image->SaveDDS(compressedPath);
+    }
+
     // todo, proper proportions
     image->Resize(64, 64);
 
-    String cachePath = db->GetCachePath();
-
     // not sure entirely what we want to do here, though if the cache file doesn't exist
     // will reimport
     image->SavePNG(cachePath + asset_->GetGUID());
@@ -72,6 +87,22 @@ bool TextureImporter::Import()
     return true;
 }
 
+void TextureImporter::ApplyProjectImportConfig()
+{
+    if (ImportConfig::IsLoaded())
+    {
+        VariantMap tiParameters;
+        ImportConfig::ApplyConfig(tiParameters);
+        VariantMap::ConstIterator itr = tiParameters.Begin();
+
+        for (; itr != tiParameters.End(); itr++)
+        {
+            if (itr->first_ == "tiProcess_CompressTextures")
+                compressTextures_ = itr->second_.GetBool();
+        }
+    }
+}
+
 bool TextureImporter::LoadSettingsInternal(JSONValue& jsonRoot)
 {
     if (!AssetImporter::LoadSettingsInternal(jsonRoot))

+ 2 - 0
Source/ToolCore/Assets/TextureImporter.h

@@ -44,10 +44,12 @@ public:
 protected:
 
     bool Import();
+    void ApplyProjectImportConfig();
 
     virtual bool LoadSettingsInternal(JSONValue& jsonRoot);
     virtual bool SaveSettingsInternal(JSONValue& jsonRoot);
 
+    bool compressTextures_;
 };
 
 }

+ 6 - 2
Source/ToolCore/Build/BuildBase.cpp

@@ -164,6 +164,11 @@ bool BuildBase::BuildCopyFile(const String& srcFileName, const String& destFileN
     return true;
 }
 
+bool BuildBase::CheckIncludeResourceFile(const String & resourceDir, const String & fileName)
+{
+    return (GetExtension(fileName) != ".psd");
+}
+
 bool BuildBase::BuildRemoveDirectory(const String& path)
 {
     if (buildFailed_)
@@ -293,8 +298,7 @@ void BuildBase::ScanResourceDirectory(const String& resourceDir)
             }
         }
 
-        // TODO: Add additional filters
-        if (GetExtension(filename) == ".psd")
+        if (!CheckIncludeResourceFile(resourceDir, filename))
             continue;
 
         BuildResourceEntry* newEntry = new BuildResourceEntry;

+ 1 - 1
Source/ToolCore/Build/BuildBase.h

@@ -70,7 +70,7 @@ protected:
     bool BuildRemoveDirectory(const String& path);
     bool BuildCreateDirectory(const String& path);
     bool BuildCopyFile(const String& srcFileName, const String& destFileName);
-
+    virtual bool CheckIncludeResourceFile(const String& resourceDir, const String& fileName);
 
     void GenerateResourcePackage(const String& resourcePackagePath);
 

+ 23 - 0
Source/ToolCore/Build/BuildMac.cpp

@@ -26,6 +26,7 @@
 #include "../ToolSystem.h"
 #include "../ToolEnvironment.h"
 #include "../Project/Project.h"
+#include "../Assets/AssetDatabase.h"
 
 #include "BuildEvents.h"
 #include "BuildSystem.h"
@@ -67,6 +68,28 @@ void BuildMac::Initialize()
 
 }
 
+bool BuildMac::CheckIncludeResourceFile(const String& resourceDir, const String& fileName)
+{
+    // #623 BEGIN TODO: Skip files that have a converted version in the cache
+    AssetDatabase* db = GetSubsystem<AssetDatabase>();
+    String cachePath = db->GetCachePath();
+    if (resourceDir != cachePath)
+    {
+        String ext = GetExtension(fileName);
+        if (ext == ".jpg" || ext == ".png" || ext == ".tga")
+        {
+            FileSystem* fileSystem = GetSubsystem<FileSystem>();
+            String compressedPath = cachePath + "DDS/" + fileName + ".dds";
+            if (fileSystem->FileExists(compressedPath))
+                return false;
+        }
+    }
+    // #623 END TODO
+
+    return BuildBase::CheckIncludeResourceFile(resourceDir, fileName);
+}
+
+
 void BuildMac::Build(const String& buildPath)
 {
     ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();

+ 1 - 0
Source/ToolCore/Build/BuildMac.h

@@ -45,6 +45,7 @@ public:
 protected:
 
     void Initialize();
+    virtual bool CheckIncludeResourceFile(const String& resourceDir, const String& fileName);
 
 
 };

+ 22 - 0
Source/ToolCore/Build/BuildWindows.cpp

@@ -27,6 +27,7 @@
 #include "../ToolSystem.h"
 #include "../ToolEnvironment.h"
 #include "../Project/Project.h"
+#include "../Assets/AssetDatabase.h"
 
 #include "BuildWindows.h"
 #include "BuildSystem.h"
@@ -68,6 +69,27 @@ void BuildWindows::Initialize()
 
 }
 
+bool BuildWindows::CheckIncludeResourceFile(const String& resourceDir, const String& fileName)
+{
+    // #623 BEGIN TODO: Skip files that have a converted version in the cache
+    AssetDatabase* db = GetSubsystem<AssetDatabase>();
+    String cachePath = db->GetCachePath();
+    if (resourceDir != cachePath)
+    {
+        String ext = GetExtension(fileName);
+        if (ext == ".jpg" || ext == ".png" || ext == ".tga")
+        {
+            FileSystem* fileSystem = GetSubsystem<FileSystem>();
+            String compressedPath = cachePath + "DDS/" + fileName + ".dds";
+            if (fileSystem->FileExists(compressedPath))
+                return false;
+        }
+    }
+    // #623 END TODO
+    
+    return BuildBase::CheckIncludeResourceFile(resourceDir, fileName);
+}
+
 void BuildWindows::BuildAtomicNET()
 {
     // AtomicNET

+ 1 - 0
Source/ToolCore/Build/BuildWindows.h

@@ -45,6 +45,7 @@ public:
 protected:
 
     void Initialize();
+    virtual bool CheckIncludeResourceFile(const String& resourceDir, const String& fileName);
 
 private:
 

+ 108 - 0
Source/ToolCore/Import/ImportConfig.cpp

@@ -0,0 +1,108 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+//
+// 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.
+//
+
+#include "../../Atomic/Core/Context.h"
+#include "../../Atomic/IO/Log.h"
+#include "../../Atomic/IO/File.h"
+#include "../../Atomic/IO/FileSystem.h"
+#include "../Atomic/Resource/JSONFile.h"
+#include "../../Atomic/Graphics/GraphicsDefs.h"
+#include "ImportConfig.h"
+
+namespace Atomic
+{
+
+ImportConfig ImportConfig::importConfig_;
+
+bool ImportConfig::LoadModelImporterConfig(const JSONValue& jModelImporterConfig)
+{
+    const JSONValue& jflags = jModelImporterConfig["aiFlagsDefault"];
+    if (!jflags.IsObject())
+        return false;
+
+    for (JSONObject::ConstIterator i = jflags.Begin(); i != jflags.End(); ++i)
+    {
+        String key = i->first_;
+        const JSONValue& jvalue = i->second_;
+
+        if (key == "convertToLeftHanded")
+            valueMap_["aiProcess_ConvertToLeftHanded"] = GetBoolValue(jvalue, true);
+        else if (key == "joinIdenticalVertices")
+            valueMap_["aiProcess_JoinIdenticalVertices"] = GetBoolValue(jvalue, true);
+        else if (key == "triangulate")
+            valueMap_["aiProcess_Triangulate"] = GetBoolValue(jvalue, true);
+        else if (key == "genSmoothNormals")
+            valueMap_["aiProcess_GenSmoothNormals"] = GetBoolValue(jvalue, true);
+        else if (key == "limitBoneWeights")
+            valueMap_["aiProcess_LimitBoneWeights"] = GetBoolValue(jvalue, true);
+        else if (key == "improveCacheLocality")
+            valueMap_["aiProcess_ImproveCacheLocality"] = GetBoolValue(jvalue, true);
+        else if (key == "fixInFacingNormals")
+            valueMap_["aiProcess_FixInfacingNormals"] = GetBoolValue(jvalue, true);
+        else if (key == "fixInfacingNormals")
+            valueMap_["aiProcess_FixInfacingNormals"] = GetBoolValue(jvalue, true);
+        else if (key == "findInvalidData")
+            valueMap_["aiProcess_FindInvalidData"] = GetBoolValue(jvalue, true);
+        else if (key == "genUVCoords")
+            valueMap_["aiProcess_GenUVCoords"] = GetBoolValue(jvalue, true);
+        else if (key == "findInstances")
+            valueMap_["aiProcess_FindInstances"] = GetBoolValue(jvalue, true);
+        else if (key == "optimizeMeshes")
+            valueMap_["aiProcess_OptimizeMeshes"] = GetBoolValue(jvalue, true);
+    }
+
+    return true;
+}
+
+bool ImportConfig::LoadTextureImporterConfig(const JSONValue& jTextureImporterConfig)
+{
+    for (JSONObject::ConstIterator i = jTextureImporterConfig.Begin(); i != jTextureImporterConfig.End(); ++i)
+    {
+        String key = i->first_;
+        const JSONValue& jvalue = i->second_;
+
+        if (key == "compressTextures")
+            valueMap_["tiProcess_CompressTextures"] = GetBoolValue(jvalue, false);
+    }
+
+    return true;
+}
+
+bool ImportConfig::LoadDesktopConfig(JSONValue root)
+{
+    const JSONValue& jdesktop = root["desktop"];
+
+    if (!jdesktop.IsObject())
+        return false;
+
+    const JSONValue& jModelImporterConfig = jdesktop["ModelImporter"];
+    if (jModelImporterConfig.IsObject())
+        LoadModelImporterConfig(jModelImporterConfig);
+ 
+    const JSONValue& jTextureImporterConfig = jdesktop["TextureImporter"];
+    if (jTextureImporterConfig.IsObject())
+        LoadTextureImporterConfig(jTextureImporterConfig);
+
+    return true;
+}
+
+}

+ 58 - 0
Source/ToolCore/Import/ImportConfig.h

@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+//
+// 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.
+//
+
+#pragma once
+
+#include "../../Atomic/Core/Variant.h"
+#include "../../Atomic/Resource/JSONValue.h"
+#include "../../Atomic/Resource/Configuration.h"
+
+namespace Atomic
+{
+
+class Context;
+
+class ImportConfig :
+    Configuration
+{
+
+public:
+
+    static bool LoadFromFile(Context* context, const String& filename) { return importConfig_.Configuration::LoadFromFile(context, filename); }
+    static bool LoadFromJSON(const String& json) { return importConfig_.Configuration::LoadFromJSON(json); }
+    static void Clear() { importConfig_.Configuration::Clear();  }
+
+    /// Apply the configuration to a setting variant map, values that exist will not be overriden
+    static void ApplyConfig(VariantMap& settings, bool overwrite = false) { return importConfig_.Configuration::ApplyConfig(settings, overwrite); }
+
+    static bool IsLoaded() { return importConfig_.Configuration::IsLoaded(); };
+
+private:
+
+    virtual bool LoadDesktopConfig(JSONValue root);
+    bool LoadModelImporterConfig(const JSONValue& jModelImporterConfig);
+    bool LoadTextureImporterConfig(const JSONValue& jTextureImporterConfig);
+
+    static ImportConfig importConfig_;
+};
+
+}

+ 58 - 0
Source/ToolCore/Import/OpenAssetImporter.cpp

@@ -40,6 +40,10 @@
 #include <Atomic/Graphics/VertexBuffer.h>
 #include <Atomic/Graphics/Material.h>
 
+#include <ToolCore/Project/Project.h>
+#include <ToolCore/ToolSystem.h>
+#include <ToolCore/Import/ImportConfig.h>
+
 #include "OpenAssetImporter.h"
 
 namespace ToolCore
@@ -86,6 +90,8 @@ OpenAssetImporter::OpenAssetImporter(Context* context) : Object(context) ,
         aiProcess_FindInstances |
         aiProcess_OptimizeMeshes;
 
+    ApplyProjectImportConfig();
+
     // TODO:  make this an option on importer
 
     aiFlagsDefault_ |= aiProcess_CalcTangentSpace;
@@ -860,6 +866,58 @@ void OpenAssetImporter::CollectAnimations(OutModel* model)
     /// \todo Vertex morphs are ignored for now
 }
 
+void OpenAssetImporter::ApplyFlag(int flag, bool active)
+{
+    aiFlagsDefault_ &= ~flag;
+    if (active)
+        aiFlagsDefault_ |= flag;
+}
+
+void OpenAssetImporter::SetOveriddenFlags(VariantMap& aiFlagParameters)
+{
+
+    VariantMap::ConstIterator itr = aiFlagParameters.Begin();
+
+    while (itr != aiFlagParameters.End())
+    {
+        if (itr->first_ == "aiProcess_ConvertToLeftHanded")
+            ApplyFlag(aiProcess_ConvertToLeftHanded, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_JoinIdenticalVertices")
+            ApplyFlag(aiProcess_JoinIdenticalVertices, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_Triangulate")
+            ApplyFlag(aiProcess_Triangulate, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_GenSmoothNormals")
+            ApplyFlag(aiProcess_GenSmoothNormals, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_LimitBoneWeights")
+            ApplyFlag(aiProcess_LimitBoneWeights, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_ImproveCacheLocality")
+            ApplyFlag(aiProcess_ImproveCacheLocality, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_FixInfacingNormals")
+            ApplyFlag(aiProcess_FixInfacingNormals, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_FindInvalidData")
+            ApplyFlag(aiProcess_FindInvalidData, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_GenUVCoords")
+            ApplyFlag(aiProcess_GenUVCoords, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_FindInstances")
+            ApplyFlag(aiProcess_FindInstances, itr->second_.GetBool());
+        else if (itr->first_ == "aiProcess_OptimizeMeshes")
+            ApplyFlag(aiProcess_OptimizeMeshes, itr->second_.GetBool());
+
+        itr++;
+    }
+
+}
+
+void OpenAssetImporter::ApplyProjectImportConfig()
+{
+    if (ImportConfig::IsLoaded())
+    {
+        VariantMap aiFlagParameters;
+        ImportConfig::ApplyConfig(aiFlagParameters);
+        SetOveriddenFlags(aiFlagParameters);
+    }
+}
+
 void OpenAssetImporter::BuildBoneCollisionInfo(OutModel& model)
 {
     for (unsigned i = 0; i < model.meshes_.Size(); ++i)

+ 4 - 0
Source/ToolCore/Import/OpenAssetImporter.h

@@ -82,6 +82,10 @@ private:
     void BuildBoneCollisionInfo(OutModel& model);
     void CollectAnimations(OutModel* model = 0);
 
+    void ApplyProjectImportConfig();
+    void SetOveriddenFlags(VariantMap& aiFlagParameters);
+    void ApplyFlag(int processStep, bool active);
+
     String GetMeshMaterialName(aiMesh* mesh);
     String GenerateMaterialName(aiMaterial* material);
     String GetMaterialTextureName(const String& nameIn);