浏览代码

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

weinand 9 年之前
父节点
当前提交
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)
 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
 # Check whether the CEF submodule is available
 if (EXISTS ${CMAKE_SOURCE_DIR}/Submodules/CEF)
 if (EXISTS ${CMAKE_SOURCE_DIR}/Submodules/CEF)

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

@@ -29,10 +29,15 @@ else()
 
 
 endif()
 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
 # 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})
 foreach(CompilerFlag ${CompilerFlags})
     string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
     string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")

+ 23 - 0
LICENSE.md

@@ -187,6 +187,29 @@ required.
 misrepresented as being the original software.
 misrepresented as being the original software.
 3. This notice may not be removed or altered from any source distribution.
 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
 #### Civetweb license
 ----------------
 ----------------

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

@@ -22,12 +22,14 @@
 
 
 import * as EditorEvents from "../editor/EditorEvents";
 import * as EditorEvents from "../editor/EditorEvents";
 import * as EditorUI from "../ui/EditorUI";
 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
  * 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[] = [];
     registeredServices: T[] = [];
 
 
     /**
     /**
@@ -38,9 +40,15 @@ class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Edit
         this.registeredServices.push(service);
         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
      * 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
      * @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
  * 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() {
     constructor() {
         super();
         super();
     }
     }
@@ -71,16 +79,18 @@ export class ProjectServiceRegistry extends ServiceRegistry<Editor.HostExtension
      * @param  {[type]} data Event info from the project unloaded event
      * @param  {[type]} data Event info from the project unloaded event
      */
      */
     projectUnloaded(data) {
     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
             // Notify services that the project has been unloaded
             try {
             try {
                 if (service.projectUnloaded) {
                 if (service.projectUnloaded) {
                     service.projectUnloaded();
                     service.projectUnloaded();
                 }
                 }
             } catch (e) {
             } 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
      * @param  {[type]} data Event info from the project unloaded event
      */
      */
     projectLoaded(ev: Editor.EditorEvents.LoadProjectEvent) {
     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 {
             try {
                 // Notify services that the project has just been loaded
                 // Notify services that the project has just been loaded
                 if (service.projectLoaded) {
                 if (service.projectLoaded) {
                     service.projectLoaded(ev);
                     service.projectLoaded(ev);
                 }
                 }
             } catch (e) {
             } 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() {
     playerStarted() {
@@ -117,7 +129,7 @@ export class ProjectServiceRegistry extends ServiceRegistry<Editor.HostExtension
 /**
 /**
  * Registry for service extensions that are concerned about Resources
  * 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() {
     constructor() {
         super();
         super();
     }
     }
@@ -145,7 +157,7 @@ export class ResourceServiceRegistry extends ServiceRegistry<Editor.HostExtensio
                     service.save(ev);
                     service.save(ev);
                 }
                 }
             } catch (e) {
             } 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);
                     service.delete(ev);
                 }
                 }
             } catch (e) {
             } 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);
                     service.rename(ev);
                 }
                 }
             } catch (e) {
             } 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 HostExtensionServices from "./HostExtensionServices";
 import * as EditorUI from "../ui/EditorUI";
 import * as EditorUI from "../ui/EditorUI";
+import ProjectBasedExtensionLoader from "./coreExtensions/ProjectBasedExtensionLoader";
 import TypescriptLanguageExtension from "./languageExtensions/TypscriptLanguageExtension";
 import TypescriptLanguageExtension from "./languageExtensions/TypscriptLanguageExtension";
 
 
 /**
 /**
@@ -31,20 +32,22 @@ import TypescriptLanguageExtension from "./languageExtensions/TypscriptLanguageE
 export class ServiceLocatorType implements Editor.HostExtensions.HostServiceLocator {
 export class ServiceLocatorType implements Editor.HostExtensions.HostServiceLocator {
 
 
     constructor() {
     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;
     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) {
     loadService(service: Editor.HostExtensions.HostEditorService) {
         try {
         try {
             service.initialize(this);
             service.initialize(this);
         } catch (e) {
         } 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.eventDispatcher = frame;
         this.resourceServices.subscribeToEvents(this);
         this.resourceServices.subscribeToEvents(this);
         this.projectServices.subscribeToEvents(this);
         this.projectServices.subscribeToEvents(this);
+        this.uiServices.subscribeToEvents(this);
     }
     }
 
 
     /**
     /**
@@ -85,4 +89,5 @@ const serviceLocator = new ServiceLocatorType();
 export default serviceLocator;
 export default serviceLocator;
 
 
 // Load up all the internal services
 // Load up all the internal services
+serviceLocator.loadService(new ProjectBasedExtensionLoader());
 serviceLocator.loadService(new TypescriptLanguageExtension());
 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
  * 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";
     name: string = "HostTypeScriptLanguageExtension";
     description: string = "This service supports the typscript webview extension.";
     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
      * Inject this language service into the registry
      * @return {[type]}             True if successful
      * @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
         // 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 MainFrame = require("./frames/MainFrame");
 import ModalOps = require("./modal/ModalOps");
 import ModalOps = require("./modal/ModalOps");
 import Shortcuts = require("./Shortcuts");
 import Shortcuts = require("./Shortcuts");
+import ServiceLocator from "../hostExtensions/ServiceLocator";
 
 
 // this is designed with public get functions to solve
 // this is designed with public get functions to solve
 // circular dependency issues in TS
 // circular dependency issues in TS
@@ -94,6 +95,14 @@ class EditorUI extends Atomic.ScriptObject {
     this.modalOps = new ModalOps();
     this.modalOps = new ModalOps();
     this.shortcuts = new Shortcuts();
     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.subscribeToEvent(EditorEvents.ModalError, (event:EditorEvents.ModalErrorEvent) => {
       this.showModalError(event.title, event.message);
       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 MainFrameMenu = require("./menus/MainFrameMenu");
 
 
 import MenuItemSources = require("./menus/MenuItemSources");
 import MenuItemSources = require("./menus/MenuItemSources");
-import ServiceLocator from "../../hostExtensions/ServiceLocator";
 import * as EditorEvents from "../../editor/EditorEvents";
 import * as EditorEvents from "../../editor/EditorEvents";
 
 
 class MainFrame extends ScriptWidget {
 class MainFrame extends ScriptWidget {
@@ -75,9 +74,6 @@ class MainFrame extends ScriptWidget {
             this.disableProjectMenus();
             this.disableProjectMenus();
         });
         });
 
 
-        // Allow the service locator to hook into the event system
-        ServiceLocator.subscribeToEvents(this);
-
         this.showWelcomeFrame(true);
         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 EditorEvents = require("editor/EditorEvents");
 import EditorUI = require("ui/EditorUI");
 import EditorUI = require("ui/EditorUI");
 import MenuItemSources = require("./MenuItemSources");
 import MenuItemSources = require("./MenuItemSources");
+import ServiceLocator from "../../../hostExtensions/ServiceLocator";
 
 
 class HierarchyFrameMenus extends Atomic.ScriptObject {
 class HierarchyFrameMenus extends Atomic.ScriptObject {
 
 
+    contentFolder: string;
+
+    private contextMenuItemSource: Atomic.UIMenuItemSource = null;
+
     constructor() {
     constructor() {
 
 
         super();
         super();
 
 
         MenuItemSources.createMenuItemSource("hierarchy create items", createItems);
         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.subscribeToEvent(EditorEvents.ContentFolderChanged, (ev: EditorEvents.ContentFolderChangedEvent) => {
             this.contentFolder = ev.path;
             this.contentFolder = ev.path;
@@ -101,6 +106,8 @@ class HierarchyFrameMenus extends Atomic.ScriptObject {
                 node.remove();
                 node.remove();
                 scene.sendEvent("SceneEditAddRemoveNodes", { end: true });
                 scene.sendEvent("SceneEditAddRemoveNodes", { end: true });
 
 
+                return true;
+
             } else if (refid == "duplicate_node") {
             } else if (refid == "duplicate_node") {
 
 
                 if (node instanceof Atomic.Scene)
                 if (node instanceof Atomic.Scene)
@@ -108,9 +115,12 @@ class HierarchyFrameMenus extends Atomic.ScriptObject {
 
 
                 var newnode = node.clone();
                 var newnode = node.clone();
                 node.scene.sendEvent("SceneEditNodeCreated", { node: newnode });
                 node.scene.sendEvent("SceneEditNodeCreated", { node: newnode });
+
+                return true;
             }
             }
 
 
-            return true;
+            // Let plugins handle context
+            return ServiceLocator.uiServices.hierarchyContextItemClicked(node, refid);
         }
         }
 
 
         return false;
         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 EditorUI = require("../../EditorUI");
 import MenuItemSources = require("./MenuItemSources");
 import MenuItemSources = require("./MenuItemSources");
 import Preferences = require("editor/Preferences");
 import Preferences = require("editor/Preferences");
+import ServiceLocator from "../../../hostExtensions/ServiceLocator";
 
 
 class MainFrameMenu extends Atomic.ScriptObject {
 class MainFrameMenu extends Atomic.ScriptObject {
 
 
+    private pluginMenuItemSource: Atomic.UIMenuItemSource;
+
     constructor() {
     constructor() {
 
 
         super();
         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 {
     handlePopupMenu(target: Atomic.UIWidget, refid: string): boolean {
 
 
         if (target.id == "menu edit popup") {
         if (target.id == "menu edit popup") {
@@ -218,12 +242,14 @@ class MainFrameMenu extends Atomic.ScriptObject {
             if (refid == "developer assetdatabase scan") {
             if (refid == "developer assetdatabase scan") {
 
 
               ToolCore.assetDatabase.scan();
               ToolCore.assetDatabase.scan();
+              return true;
 
 
             }
             }
 
 
             if (refid == "developer assetdatabase force") {
             if (refid == "developer assetdatabase force") {
 
 
               ToolCore.assetDatabase.reimportAllAssets();
               ToolCore.assetDatabase.reimportAllAssets();
+              return true;
 
 
             }
             }
 
 
@@ -234,8 +260,12 @@ class MainFrameMenu extends Atomic.ScriptObject {
                 myPrefs.saveEditorWindowData(myPrefs.editorWindow);
                 myPrefs.saveEditorWindowData(myPrefs.editorWindow);
                 myPrefs.savePlayerWindowData(myPrefs.playerWindow);
                 myPrefs.savePlayerWindowData(myPrefs.playerWindow);
                 Atomic.getEngine().exit();
                 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") {
         } else if (target.id == "menu tools popup") {
 
 
             if (refid == "tools toggle profiler") {
             if (refid == "tools toggle profiler") {
@@ -286,6 +316,8 @@ class MainFrameMenu extends Atomic.ScriptObject {
                 return true;
                 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") {
             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 {
 export function createMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
 
 
     srcLookup[id] = createMenuItemSourceRecursive(items);
     srcLookup[id] = createMenuItemSourceRecursive(items);
@@ -102,3 +107,9 @@ export function createMenuItemSource(id: string, items: any): Atomic.UIMenuItemS
     return srcLookup[id];
     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 EditorEvents = require("editor/EditorEvents");
 import EditorUI = require("ui/EditorUI");
 import EditorUI = require("ui/EditorUI");
 import MenuItemSources = require("./MenuItemSources");
 import MenuItemSources = require("./MenuItemSources");
+import ServiceLocator from "../../../hostExtensions/ServiceLocator";
 
 
 class ProjectFrameMenus extends Atomic.ScriptObject {
 class ProjectFrameMenus extends Atomic.ScriptObject {
 
 
+    contentFolder: string;
+
+    private contextMenuItemSource: Atomic.UIMenuItemSource = null;
+
     constructor() {
     constructor() {
 
 
         super();
         super();
 
 
         MenuItemSources.createMenuItemSource("asset context folder", assetFolderContextItems);
         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);
         MenuItemSources.createMenuItemSource("project create items", createItems);
 
 
         this.subscribeToEvent(EditorEvents.ContentFolderChanged, (ev: EditorEvents.ContentFolderChangedEvent) => {
         this.subscribeToEvent(EditorEvents.ContentFolderChanged, (ev: EditorEvents.ContentFolderChangedEvent) => {
@@ -106,6 +111,8 @@ class ProjectFrameMenus extends Atomic.ScriptObject {
                 return true;
                 return true;
             }
             }
 
 
+            // Let plugins handle context
+            return ServiceLocator.uiServices.projectContextItemClicked(asset, refid);
         }
         }
 
 
         return false;
         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 SnapSettingsWindow = require("./SnapSettingsWindow");
 
 
+import ExtensionWindow = require("./ExtensionWindow");
+
 import ProjectTemplates = require("../../resources/ProjectTemplates");
 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 {
     private show(): boolean {
 
 
         if (this.dimmer.parent) {
         if (this.dimmer.parent) {

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

@@ -26,7 +26,7 @@
 export default class ClientExtensionEventNames {
 export default class ClientExtensionEventNames {
     static CodeLoadedEvent = "CodeLoadedEvent";
     static CodeLoadedEvent = "CodeLoadedEvent";
     static ConfigureEditorEvent = "ConfigureEditorEvent";
     static ConfigureEditorEvent = "ConfigureEditorEvent";
-    static ResourceSavedEvent = "ResourceSavedEvent";
+    static CodeSavedEvent = "CodeSavedEvent";
     static ResourceRenamedEvent = "ResourceRenamedEvent";
     static ResourceRenamedEvent = "ResourceRenamedEvent";
     static ResourceDeletedEvent = "ResourceDeletedEvent";
     static ResourceDeletedEvent = "ResourceDeletedEvent";
     static ProjectUnloadedEvent = "ProjectUnloadedEvent";
     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
  * 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[] = [];
     registeredServices: T[] = [];
 
 
     /**
     /**
@@ -64,6 +64,13 @@ class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Edit
     register(service: T) {
     register(service: T) {
         this.registeredServices.push(service);
         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> {
 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.ResourceRenamedEvent, (ev) => this.renameResource(ev));
         eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ProjectUnloadedEvent, (ev) => this.projectUnloaded());
         eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ProjectUnloadedEvent, (ev) => this.projectUnloaded());
         eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ResourceDeletedEvent, (ev) => this.deleteResource(ev));
         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
      * @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.
         // run through and find any services that can handle this.
         this.registeredServices.forEach((service) => {
         this.registeredServices.forEach((service) => {
             try {
             try {

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

@@ -144,9 +144,26 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
             case WorkerProcessTypes.Alert:
             case WorkerProcessTypes.Alert:
                 alert(e.data.message);
                 alert(e.data.message);
                 break;
                 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
      * 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
      * Called once a resource has been saved
      * @param  {Editor.EditorEvents.SaveResourceEvent} ev
      * @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 = {
             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);
             this.worker.port.postMessage(message);

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

@@ -163,6 +163,26 @@ export class TypescriptLanguageService {
     getProjectFiles(): string[] {
     getProjectFiles(): string[] {
         return this.projectFiles;
         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
      * Simply transpile the typescript file.  This is much faster and only checks for syntax errors
      * @param {string[]}           fileNames array of files to transpile
      * @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  {string}  a list of file names to compile
      * @param  {ts.CompilerOptions} options for the compiler
      * @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();
         let start = new Date().getTime();
 
 
         options = options || this.compilerOptions;
         options = options || this.compilerOptions;
@@ -242,18 +262,12 @@ export class TypescriptLanguageService {
         } else {
         } else {
             // Only compile the files that are newly edited
             // Only compile the files that are newly edited
             files.forEach(filename => {
             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));
                 errors = errors.concat(this.compileFile(filename));
             });
             });
         }
         }
 
 
-        if (errors.length) {
-            this.logErrors(errors);
-        }
-
         console.log(`${this.name}: Compiling complete after ${new Date().getTime() - start} ms`);
         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);
         let idx = this.projectFiles.indexOf(filepath);
         if (idx > -1) {
         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;
             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:
                 case WorkerProcessTypes.GetDocTooltip:
                     this.handleGetDocTooltip(port, e.data);
                     this.handleGetDocTooltip(port, e.data);
                     break;
                     break;
-                case ClientExtensionEventNames.ResourceSavedEvent:
+                case ClientExtensionEventNames.CodeSavedEvent:
                     this.handleSave(port, e.data);
                     this.handleSave(port, e.data);
                     break;
                     break;
                 case ClientExtensionEventNames.ResourceRenamedEvent:
                 case ClientExtensionEventNames.ResourceRenamedEvent:
@@ -180,6 +180,9 @@ export default class TypescriptLanguageServiceWebWorker {
                 case ClientExtensionEventNames.ProjectUnloadedEvent:
                 case ClientExtensionEventNames.ProjectUnloadedEvent:
                     this.handleProjectUnloaded(port);
                     this.handleProjectUnloaded(port);
                     break;
                     break;
+                case WorkerProcessTypes.GetAnnotations:
+                    this.handleGetAnnotations(port, e.data);
+                    break;
             }
             }
 
 
         }, false);
         }, false);
@@ -247,7 +250,8 @@ export default class TypescriptLanguageServiceWebWorker {
     }) {
     }) {
         port.postMessage({ command: WorkerProcessTypes.Message, message: "Hello " + eventData.sender + " (port #" + this.connections + ")" });
         port.postMessage({ command: WorkerProcessTypes.Message, message: "Hello " + eventData.sender + " (port #" + this.connections + ")" });
         this.loadProjectFiles().then(() => {
         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) {
         if (details) {
             let docs = details.displayParts.map(part => part.text).join("");
             let docs = details.displayParts.map(part => part.text).join("");
             if (details.documentation) {
             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;
             message.docHTML = docs;
@@ -323,16 +327,23 @@ export default class TypescriptLanguageServiceWebWorker {
         port.postMessage(message);
         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  {MessagePort} port
      * @param  {WorkerProcessCommands.SaveMessageData} eventData
      * @param  {WorkerProcessCommands.SaveMessageData} eventData
      */
      */
     handleSave(port: MessagePort, eventData: WorkerProcessTypes.SaveMessageData) {
     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;
     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 GetCompletions = "COMPLETIONS";
 export const CompletionResponse = "COMPLETION_RESPONSE";
 export const CompletionResponse = "COMPLETION_RESPONSE";
 export interface WordCompletion {
 export interface WordCompletion {
@@ -65,11 +69,14 @@ export interface GetDocTooltipResponseMessageData extends WorkerProcessMessageDa
     docHTML?: string;
     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 Connect = "HELO";
 export const Disconnect = "CLOSE";
 export const Disconnect = "CLOSE";
 export const Message = "MESSAGE";
 export const Message = "MESSAGE";
 export const Alert = "ALERT";
 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);
     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 {
 export default class HostInteropType {
 
 
     private static _inst: HostInteropType = null;
     private static _inst: HostInteropType = null;
+    private fileName: string = null;
+    private fileExt: string = null;
+
     static getInstance(): HostInteropType {
     static getInstance(): HostInteropType {
         if (HostInteropType._inst == null) {
         if (HostInteropType._inst == null) {
             HostInteropType._inst = new HostInteropType();
             HostInteropType._inst = new HostInteropType();
@@ -90,9 +93,13 @@ export default class HostInteropType {
      */
      */
     loadCode(codeUrl: string) {
     loadCode(codeUrl: string) {
         console.log("Load Code called for :" + codeUrl);
         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://", "");
         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
         // go ahead and set the theme prior to pulling the file across
         editorCommands.configure(fileExt, filename);
         editorCommands.configure(fileExt, filename);
 
 
@@ -108,10 +115,13 @@ export default class HostInteropType {
      * Save the contents of the editor
      * Save the contents of the editor
      * @return {Promise}
      * @return {Promise}
      */
      */
-    saveCode(): Promise<{}> {
+    saveCode(): Promise<any> {
+        let source = editorCommands.getSourceText();
         return atomicQueryPromise({
         return atomicQueryPromise({
             message: HostInteropType.EDITOR_SAVE_CODE,
             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
      * @param  {string} fileContents
      * @return {Promise}
      * @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({
         return atomicQueryPromise({
             message: HostInteropType.EDITOR_SAVE_FILE,
             message: HostInteropType.EDITOR_SAVE_FILE,
             filename: filename,
             filename: filename,
             payload: fileContents
             payload: fileContents
+        }).then(() => {
+            editorCommands.codeSaved(filename, fileExt, fileContents);
         });
         });
     }
     }
 
 
@@ -189,6 +202,7 @@ export default class HostInteropType {
      * @param  {string} newPath
      * @param  {string} newPath
      */
      */
     resourceRenamed(path: string, newPath: string) {
     resourceRenamed(path: string, newPath: string) {
+        this.fileName = newPath;
         editorCommands.resourceRenamed(path, newPath);
         editorCommands.resourceRenamed(path, newPath);
     }
     }
 
 

+ 1 - 0
Script/AtomicWebViewEditor/tsconfig.json

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

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

@@ -19,8 +19,8 @@
 
 
 		"Object" : [
 		"Object" : [
 			"sendEvent(eventType:string, data?: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" : {
 	"haxe_decl" : {

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

@@ -39,14 +39,21 @@
 			"getChildrenWithName(name:string, recursive?:boolean):Node[];",
 			"getChildrenWithName(name:string, recursive?:boolean):Node[];",
 			"getChildrenWithComponent(componentType:string, recursive?:boolean):Node[];",
 			"getChildrenWithComponent(componentType:string, recursive?:boolean):Node[];",
 			"getComponents(componentType?:string, recursive?:boolean):Component[];",
 			"getComponents(componentType?:string, recursive?:boolean):Component[];",
+			"getComponent<T extends Atomic.Component>(type: string): T;",
 			"getChildAtIndex(index:number):Node;",
 			"getChildAtIndex(index:number):Node;",
 			"createJSComponent(name:string, args?:{});",
 			"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;",
 			"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" : [
 		"Scene" : [
 			"getMainCamera():Camera;"
 			"getMainCamera():Camera;"
+		],
+		"Component": [
+		    "getComponent<T extends Atomic.Component>(type: string): T;"
 		]
 		]
 	},
 	},
 	"haxe_decl" : {
 	"haxe_decl" : {

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

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

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

@@ -5,7 +5,9 @@
 // license information: https://github.com/AtomicGameEngine/AtomicGameEngine
 // license information: https://github.com/AtomicGameEngine/AtomicGameEngine
 //
 //
 
 
+/// <reference path="Atomic.d.ts" />
 /// <reference path="Editor.d.ts" />
 /// <reference path="Editor.d.ts" />
+/// <reference path="ToolCore.d.ts" />
 
 
 declare module Editor.EditorEvents {
 declare module Editor.EditorEvents {
 
 
@@ -76,6 +78,10 @@ declare module Editor.EditorEvents {
         code: string;
         code: string;
     }
     }
 
 
+    export interface CodeSavedEvent extends EditorFileEvent {
+        code: string;
+    }
+
     export interface EditorCloseResourceEvent {
     export interface EditorCloseResourceEvent {
 
 
         editor: Editor.ResourceEditor;
         editor: Editor.ResourceEditor;
@@ -141,7 +147,7 @@ declare module Editor.Extensions {
     /**
     /**
      * Base interface for any editor services.
      * Base interface for any editor services.
      */
      */
-    export interface EditorService {
+    export interface EditorServiceExtension {
         /**
         /**
          * Unique name of this service
          * Unique name of this service
          * @type {string}
          * @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 {
     interface EventDispatcher {
         /**
         /**
          * Send a custom event.  This can be used by services to publish custom events
          * 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
          * Loads a service into a service registry
          * @param  {EditorService} service
          * @param  {EditorService} service
          */
          */
-        loadService(service: EditorService): void;
+        loadService(service: EditorServiceExtension): void;
     }
     }
 
 
     /**
     /**
      * Service registry interface for registering services
      * Service registry interface for registering services
      */
      */
-    export interface ServiceRegistry<T extends EditorService> {
+    export interface ServicesProvider<T extends ServiceEventListener> {
         registeredServices: T[];
         registeredServices: T[];
 
 
         /**
         /**
@@ -195,6 +207,17 @@ declare module Editor.Extensions {
          * @param  {T}      service the service to register
          * @param  {T}      service the service to register
          */
          */
         register(service: T);
         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.
      * or by the editor itself.
      */
      */
     export interface HostServiceLocator extends Editor.Extensions.ServiceLoader {
     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
          * 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);
         save?(ev: EditorEvents.SaveResourceEvent);
         delete?(ev: EditorEvents.DeleteResourceEvent);
         delete?(ev: EditorEvents.DeleteResourceEvent);
         rename?(ev: EditorEvents.RenameResourceEvent);
         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?();
         projectUnloaded?();
         projectLoaded?(ev: EditorEvents.LoadProjectEvent);
         projectLoaded?(ev: EditorEvents.LoadProjectEvent);
         playerStarted?();
         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;
         getHostInterop(): HostInterop;
     }
     }
 
 
-    export interface ClientEditorService extends Editor.Extensions.EditorService {
+    export interface ClientEditorService extends Editor.Extensions.EditorServiceExtension {
         /**
         /**
          * Called by the service locator at load time
          * Called by the service locator at load time
          */
          */
         initialize(serviceLocator: ClientServiceLocator);
         initialize(serviceLocator: ClientServiceLocator);
     }
     }
 
 
-    export interface WebViewService extends Editor.Extensions.EditorService {
+    export interface WebViewService extends Editor.Extensions.EditorServiceExtension {
         configureEditor?(ev: EditorEvents.EditorFileEvent);
         configureEditor?(ev: EditorEvents.EditorFileEvent);
         codeLoaded?(ev: EditorEvents.CodeLoadedEvent);
         codeLoaded?(ev: EditorEvents.CodeLoadedEvent);
-        save?(ev: EditorEvents.SaveResourceEvent);
+        save?(ev: EditorEvents.CodeSavedEvent);
         delete?(ev: EditorEvents.DeleteResourceEvent);
         delete?(ev: EditorEvents.DeleteResourceEvent);
         rename?(ev: EditorEvents.RenameResourceEvent);
         rename?(ev: EditorEvents.RenameResourceEvent);
         projectUnloaded?();
         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/EditorEvents.ts",
         "./AtomicEditor/editor/EditorLicense.ts",
         "./AtomicEditor/editor/EditorLicense.ts",
         "./AtomicEditor/editor/Preferences.ts",
         "./AtomicEditor/editor/Preferences.ts",
+        "./AtomicEditor/hostExtensions/coreExtensions/ProjectBasedExtensionLoader.ts",
         "./AtomicEditor/hostExtensions/HostExtensionServices.ts",
         "./AtomicEditor/hostExtensions/HostExtensionServices.ts",
         "./AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts",
         "./AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts",
         "./AtomicEditor/hostExtensions/ServiceLocator.ts",
         "./AtomicEditor/hostExtensions/ServiceLocator.ts",
@@ -75,6 +76,7 @@
         "./AtomicEditor/ui/modal/build/platforms/WebSettingsWidget.ts",
         "./AtomicEditor/ui/modal/build/platforms/WebSettingsWidget.ts",
         "./AtomicEditor/ui/modal/build/platforms/WindowsSettingsWidget.ts",
         "./AtomicEditor/ui/modal/build/platforms/WindowsSettingsWidget.ts",
         "./AtomicEditor/ui/modal/CreateProject.ts",
         "./AtomicEditor/ui/modal/CreateProject.ts",
+        "./AtomicEditor/ui/modal/ExtensionWindow.ts",
         "./AtomicEditor/ui/modal/license/ActivationSuccessWindow.ts",
         "./AtomicEditor/ui/modal/license/ActivationSuccessWindow.ts",
         "./AtomicEditor/ui/modal/license/ActivationWindow.ts",
         "./AtomicEditor/ui/modal/license/ActivationWindow.ts",
         "./AtomicEditor/ui/modal/license/EULAWindow.ts",
         "./AtomicEditor/ui/modal/license/EULAWindow.ts",
@@ -98,6 +100,7 @@
         "./TypeScript/AtomicNET.d.ts",
         "./TypeScript/AtomicNET.d.ts",
         "./TypeScript/AtomicPlayer.d.ts",
         "./TypeScript/AtomicPlayer.d.ts",
         "./TypeScript/AtomicWork.d.ts",
         "./TypeScript/AtomicWork.d.ts",
+        "./TypeScript/duktape.d.ts",
         "./TypeScript/Editor.d.ts",
         "./TypeScript/Editor.d.ts",
         "./TypeScript/EditorWork.d.ts",
         "./TypeScript/EditorWork.d.ts",
         "./TypeScript/ToolCore.d.ts",
         "./TypeScript/ToolCore.d.ts",

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

@@ -31,38 +31,7 @@
 namespace Atomic
 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)
 bool EngineConfig::LoadEngineConfig(const JSONValue& jengine)
 {
 {
@@ -75,11 +44,11 @@ bool EngineConfig::LoadEngineConfig(const JSONValue& jengine)
         const JSONValue& jvalue = i->second_;
         const JSONValue& jvalue = i->second_;
 
 
         if (key == "workerthreads")
         if (key == "workerthreads")
-            engineConfig_["WorkerThreads"] = GetBoolValue(jvalue, true);
+            valueMap_["WorkerThreads"] = GetBoolValue(jvalue, true);
         else if (key == "logquiet")
         else if (key == "logquiet")
-            engineConfig_["LogQuiet"] = GetBoolValue(jvalue, false);
+            valueMap_["LogQuiet"] = GetBoolValue(jvalue, false);
         else if (key == "loglevel")
         else if (key == "loglevel")
-            engineConfig_["LogLevel"] = GetIntValue(jvalue, 1);
+            valueMap_["LogLevel"] = GetIntValue(jvalue, 1);
     }
     }
 
 
     return true;
     return true;
@@ -96,74 +65,74 @@ bool EngineConfig::LoadGraphicsConfig(const JSONValue& jgraphics)
         const JSONValue& jvalue = i->second_;
         const JSONValue& jvalue = i->second_;
 
 
         if (key == "headless")
         if (key == "headless")
-            engineConfig_["Headless"] = GetBoolValue(jvalue, false);
+            valueMap_["Headless"] = GetBoolValue(jvalue, false);
         else if (key == "framelimiter")
         else if (key == "framelimiter")
-            engineConfig_["FrameLimiter"] = GetBoolValue(jvalue, true);
+            valueMap_["FrameLimiter"] = GetBoolValue(jvalue, true);
         else if (key == "flushgpu")
         else if (key == "flushgpu")
-            engineConfig_["FlushGPU"] = GetBoolValue(jvalue, false);
+            valueMap_["FlushGPU"] = GetBoolValue(jvalue, false);
         else if (key == "forcegl2")
         else if (key == "forcegl2")
-            engineConfig_["ForceGL2"] = GetBoolValue(jvalue, false);
+            valueMap_["ForceGL2"] = GetBoolValue(jvalue, false);
         else if (key == "orientations")
         else if (key == "orientations")
-            engineConfig_["Orientations"] = GetStringValue(jvalue, "LandscapeLeft LandscapeRight");
+            valueMap_["Orientations"] = GetStringValue(jvalue, "LandscapeLeft LandscapeRight");
         else if (key == "vsync")
         else if (key == "vsync")
-            engineConfig_["VSync"] = GetBoolValue(jvalue, false);
+            valueMap_["VSync"] = GetBoolValue(jvalue, false);
         else if (key == "triplebuffer")
         else if (key == "triplebuffer")
-            engineConfig_["TripleBuffer"] = GetBoolValue(jvalue, false);
+            valueMap_["TripleBuffer"] = GetBoolValue(jvalue, false);
         else if (key == "multisample")
         else if (key == "multisample")
-            engineConfig_["Multisample"] = GetIntValue(jvalue, 1);
+            valueMap_["Multisample"] = GetIntValue(jvalue, 1);
         else if (key == "renderpath")
         else if (key == "renderpath")
         {
         {
             String renderPath = GetStringValue(jvalue, "forward").ToLower();
             String renderPath = GetStringValue(jvalue, "forward").ToLower();
 
 
             if (renderPath == "forward")
             if (renderPath == "forward")
-                engineConfig_["RenderPath"] = "RenderPaths/Forward.xml";
+                valueMap_["RenderPath"] = "RenderPaths/Forward.xml";
             else if (renderPath == "prepass")
             else if (renderPath == "prepass")
-                engineConfig_["RenderPath"] = "RenderPaths/Prepass.xml";
+                valueMap_["RenderPath"] = "RenderPaths/Prepass.xml";
             else if (renderPath == "deferred")
             else if (renderPath == "deferred")
-                engineConfig_["RenderPath"] = "RenderPaths/Deferred.xml";
+                valueMap_["RenderPath"] = "RenderPaths/Deferred.xml";
         }
         }
         else if (key == "shadows")
         else if (key == "shadows")
-            engineConfig_["Shadows"] = GetBoolValue(jvalue, true);
+            valueMap_["Shadows"] = GetBoolValue(jvalue, true);
         else if (key == "lowqualityshadows")
         else if (key == "lowqualityshadows")
-            engineConfig_["LowQualityShadows"] = GetBoolValue(jvalue, false);
+            valueMap_["LowQualityShadows"] = GetBoolValue(jvalue, false);
         else if (key == "materialquality")
         else if (key == "materialquality")
         {
         {
             String quality = GetStringValue(jvalue, "high").ToLower();
             String quality = GetStringValue(jvalue, "high").ToLower();
 
 
             if (quality == "high")
             if (quality == "high")
-                engineConfig_["MaterialQuality"] = QUALITY_HIGH;
+                valueMap_["MaterialQuality"] = QUALITY_HIGH;
             else if (quality == "medium")
             else if (quality == "medium")
-                engineConfig_["MaterialQuality"] = QUALITY_MEDIUM;
+                valueMap_["MaterialQuality"] = QUALITY_MEDIUM;
             else if (quality == "low")
             else if (quality == "low")
-                engineConfig_["MaterialQuality"] = QUALITY_LOW;
+                valueMap_["MaterialQuality"] = QUALITY_LOW;
         }
         }
         else if (key == "texturequality")
         else if (key == "texturequality")
         {
         {
             String quality = GetStringValue(jvalue, "high").ToLower();
             String quality = GetStringValue(jvalue, "high").ToLower();
 
 
             if (quality == "high")
             if (quality == "high")
-                engineConfig_["TextureQuality"] = QUALITY_HIGH;
+                valueMap_["TextureQuality"] = QUALITY_HIGH;
             else if (quality == "medium")
             else if (quality == "medium")
-                engineConfig_["TextureQuality"] = QUALITY_MEDIUM;
+                valueMap_["TextureQuality"] = QUALITY_MEDIUM;
             else if (quality == "low")
             else if (quality == "low")
-                engineConfig_["TextureQuality"] = QUALITY_LOW;
+                valueMap_["TextureQuality"] = QUALITY_LOW;
         }
         }
         else if (key == "texturefiltermode")
         else if (key == "texturefiltermode")
         {
         {
             String mode = GetStringValue(jvalue, "trilinear").ToLower();
             String mode = GetStringValue(jvalue, "trilinear").ToLower();
 
 
             if (mode == "trilinear")
             if (mode == "trilinear")
-                engineConfig_["TextureFilterMode"] = FILTER_TRILINEAR;
+                valueMap_["TextureFilterMode"] = FILTER_TRILINEAR;
             else if (mode == "bilinear")
             else if (mode == "bilinear")
-                engineConfig_["TextureFilterMode"] = FILTER_BILINEAR;
+                valueMap_["TextureFilterMode"] = FILTER_BILINEAR;
             else if (mode == "nearest")
             else if (mode == "nearest")
-                engineConfig_["TextureFilterMode"] = FILTER_NEAREST;
+                valueMap_["TextureFilterMode"] = FILTER_NEAREST;
             else if (mode == "anisotropic")
             else if (mode == "anisotropic")
-                engineConfig_["TextureFilterMode"] = FILTER_ANISOTROPIC;
+                valueMap_["TextureFilterMode"] = FILTER_ANISOTROPIC;
         }
         }
         else if (key == "textureanisotropy")
         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_;
         const JSONValue& jvalue = i->second_;
 
 
         if (key == "title")
         if (key == "title")
-            engineConfig_["WindowTitle"] = GetStringValue(jvalue, "Atomic");
+            valueMap_["WindowTitle"] = GetStringValue(jvalue, "Atomic");
         else if (key == "fullscreen")
         else if (key == "fullscreen")
-            engineConfig_["FullScreen"] = GetBoolValue(jvalue, false);
+            valueMap_["FullScreen"] = GetBoolValue(jvalue, false);
         else if (key == "borderless")
         else if (key == "borderless")
-            engineConfig_["Borderless"] = GetBoolValue(jvalue, false);
+            valueMap_["Borderless"] = GetBoolValue(jvalue, false);
         else if (key == "resizable")
         else if (key == "resizable")
-            engineConfig_["WindowResizable"] = GetBoolValue(jvalue, false);
+            valueMap_["WindowResizable"] = GetBoolValue(jvalue, false);
         else if (key == "width")
         else if (key == "width")
-            engineConfig_["WindowWidth"] = GetIntValue(jvalue, false);
+            valueMap_["WindowWidth"] = GetIntValue(jvalue, false);
         else if (key == "height")
         else if (key == "height")
-            engineConfig_["WindowHeight"] = GetIntValue(jvalue, false);
+            valueMap_["WindowHeight"] = GetIntValue(jvalue, false);
         else if (key == "positionx")
         else if (key == "positionx")
-            engineConfig_["WindowPositionX"] = GetIntValue(jvalue, false);
+            valueMap_["WindowPositionX"] = GetIntValue(jvalue, false);
         else if (key == "positiony")
         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_;
         const JSONValue& jvalue = i->second_;
 
 
         if (key == "enabled")
         if (key == "enabled")
-            engineConfig_["Sound"] = GetBoolValue(jvalue, true);
+            valueMap_["Sound"] = GetBoolValue(jvalue, true);
         else if (key == "interpolation")
         else if (key == "interpolation")
-            engineConfig_["SoundInterpolation"] = GetBoolValue(jvalue, true);
+            valueMap_["SoundInterpolation"] = GetBoolValue(jvalue, true);
         else if (key == "stereo")
         else if (key == "stereo")
-            engineConfig_["SoundStereo"] = GetBoolValue(jvalue, true);
+            valueMap_["SoundStereo"] = GetBoolValue(jvalue, true);
         else if (key == "bufferms")
         else if (key == "bufferms")
-            engineConfig_["SoundBuffer"] = GetIntValue(jvalue, 100);
+            valueMap_["SoundBuffer"] = GetIntValue(jvalue, 100);
         else if (key == "mixrate")
         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_;
         const JSONValue& jvalue = i->second_;
 
 
         if (key == "touchemulation")
         if (key == "touchemulation")
-            engineConfig_["TouchEmulation"] = GetBoolValue(jvalue, false);
+            valueMap_["TouchEmulation"] = GetBoolValue(jvalue, false);
     }
     }
 
 
     return true;
     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)
 bool EngineConfig::LoadDesktopConfig(JSONValue root)
 {
 {
@@ -278,74 +268,12 @@ bool EngineConfig::LoadDesktopConfig(JSONValue root)
     if (jinput.IsObject())
     if (jinput.IsObject())
         LoadInputConfig(jinput);
         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;
     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 "../Core/Variant.h"
 #include "../Resource/JSONValue.h"
 #include "../Resource/JSONValue.h"
+#include "../Resource/Configuration.h"
 
 
 namespace Atomic
 namespace Atomic
 {
 {
@@ -31,36 +32,31 @@ namespace Atomic
 class Context;
 class Context;
 
 
 /// Atomic engine configuration
 /// Atomic engine configuration
-class EngineConfig
+class EngineConfig :
+    Configuration
 {
 {
 
 
 public:
 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
     /// 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:
 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 "../../IO/Log.h"
 #include "../../Resource/ResourceCache.h"
 #include "../../Resource/ResourceCache.h"
 
 
+#ifdef ATOMIC_D3DCOMPILER_ENABLED
 #include <d3dcompiler.h>
 #include <d3dcompiler.h>
+#endif
+
 #include <MojoShader/mojoshader.h>
 #include <MojoShader/mojoshader.h>
 
 
 #include "../../DebugNew.h"
 #include "../../DebugNew.h"
@@ -220,6 +223,10 @@ bool ShaderVariation::LoadByteCode(PODVector<unsigned>& byteCode, const String&
 
 
 bool ShaderVariation::Compile(PODVector<unsigned>& byteCode)
 bool ShaderVariation::Compile(PODVector<unsigned>& byteCode)
 {
 {
+
+#ifndef ATOMIC_D3DCOMPILER_ENABLED
+    return false;
+#else
     const String& sourceCode = owner_->GetSourceCode(type_);
     const String& sourceCode = owner_->GetSourceCode(type_);
     Vector<String> defines = defines_.Split(' ');
     Vector<String> defines = defines_.Split(' ');
 
 
@@ -306,6 +313,7 @@ bool ShaderVariation::Compile(PODVector<unsigned>& byteCode)
         errorMsgs->Release();
         errorMsgs->Release();
 
 
     return !byteCode.Empty();
     return !byteCode.Empty();
+#endif
 }
 }
 
 
 void ShaderVariation::ParseParameters(unsigned char* bufData, unsigned bufSize)
 void ShaderVariation::ParseParameters(unsigned char* bufData, unsigned bufSize)

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

@@ -33,6 +33,13 @@
 #include "IPC.h"
 #include "IPC.h"
 #include "IPCEvents.h"
 #include "IPCEvents.h"
 
 
+#if defined(ATOMIC_PLATFORM_WINDOWS)
+
+#include <windows.h>
+#undef PostMessage
+
+#endif
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 
@@ -40,6 +47,28 @@ IPC::IPC(Context* context) : Object(context),
     workerChannelID_(0)
     workerChannelID_(0)
 {
 {
     SubscribeToEvent(E_BEGINFRAME, HANDLER(IPC, HandleBeginFrame));
     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()
 IPC::~IPC()

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

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

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

@@ -29,8 +29,11 @@
 #include <string>
 #include <string>
 
 
 #include "../Core/Timer.h"
 #include "../Core/Timer.h"
+#include "../IO/Log.h"
 #include "IPCWindows.h"
 #include "IPCWindows.h"
 
 
+#include "IPC.h"
+
 typedef std::wstring IPCWString;
 typedef std::wstring IPCWString;
 
 
 namespace Atomic
 namespace Atomic
@@ -303,6 +306,17 @@ bool IPCProcess::Launch(const String& command, const Vector<String>& args, const
         return false;
         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;
     pid_ = pi.hProcess;
     ::CloseHandle(pi.hThread);
     ::CloseHandle(pi.hThread);
 
 

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

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

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

@@ -101,10 +101,16 @@ void IPCWorker::ThreadFunction()
 {
 {
     while (shouldRun_)
     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())
         if (!otherProcess_->IsRunning())
         {
         {
             break;
             break;
         }
         }
+#endif
 
 
         if (!Receive())
         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"
 #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);
 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
 #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 = 77;
 static const unsigned DDS_DXGI_FORMAT_BC3_UNORM_SRGB = 78;
 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
 namespace Atomic
 {
 {
 
 
@@ -1211,6 +1234,136 @@ bool Image::SaveJPG(const String& fileName, int quality) const
         return false;
         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
 Color Image::GetPixel(int x, int y) const
 {
 {
     return GetPixel(x, y, 0);
     return GetPixel(x, y, 0);

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

@@ -138,12 +138,18 @@ public:
     bool SaveTGA(const String& fileName) const;
     bool SaveTGA(const String& fileName) const;
     /// Save in JPG format with compression quality. Return true if successful.
     /// Save in JPG format with compression quality. Return true if successful.
     bool SaveJPG(const String& fileName, int quality) const;
     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.
     /// Whether this texture is detected as a cubemap, only relevant for DDS.
     bool IsCubemap() const { return cubemap_; }
     bool IsCubemap() const { return cubemap_; }
     /// Whether this texture has been detected as a volume, only relevant for DDS.
     /// Whether this texture has been detected as a volume, only relevant for DDS.
     bool IsArray() const { return array_; }
     bool IsArray() const { return array_; }
     /// Whether this texture is in sRGB, only relevant for DDS.
     /// Whether this texture is in sRGB, only relevant for DDS.
     bool IsSRGB() const { return sRGB_; }
     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.
     /// Return a 2D pixel color.
     Color GetPixel(int x, int y) const;
     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 (document.Parse<0>(json.CString()).HasParseError())
     {
     {
         if (reportError)
         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;
         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
     // 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)
     if (!file)
         return 0;   // Error is already logged
         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()
 TBSelectItemSource *UISelectItemSource::GetTBItemSource()
 {
 {
     // caller's responsibility to clean up
     // 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 SetString(const String& str) { str_ = str; }
     void SetID(const String& id);
     void SetID(const String& id);
+    const String& GetStr() { return str_; }
+    tb::TBID GetID() { return id_; }
     void SetSkinImage(const String& skinImage);
     void SetSkinImage(const String& skinImage);
     void SetSubSource(UISelectItemSource *subSource);
     void SetSubSource(UISelectItemSource *subSource);
 
 
@@ -72,6 +74,9 @@ public:
     virtual ~UISelectItemSource();
     virtual ~UISelectItemSource();
 
 
     void AddItem(UISelectItem* item) { items_.Push(SharedPtr<UISelectItem>(item)); }
     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(); }
     void Clear() { items_.Clear(); }
 
 

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

@@ -54,7 +54,7 @@
 namespace Atomic
 namespace Atomic
 {
 {
     void jsapi_init_atomicnet(JSVM* vm);
     void jsapi_init_atomicnet(JSVM* vm);
-    void jsapi_init_webview(JSVM* vm);;
+    void jsapi_init_webview(JSVM* vm, const VariantMap& engineParameters);
 }
 }
 
 
 using namespace ToolCore;
 using namespace ToolCore;
@@ -88,9 +88,8 @@ void AEEditorCommon::Start()
     jsapi_init_toolcore(vm_);
     jsapi_init_toolcore(vm_);
 
 
 #ifdef ATOMIC_WEBVIEW
 #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
 #endif
 
 
 
 

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

@@ -37,10 +37,10 @@
 namespace AtomicEditor
 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>();
     ResourceCache* cache = GetSubsystem<ResourceCache>();
 
 
@@ -185,10 +185,8 @@ void Gizmo3D::Use()
     Ray cameraRay = view3D_->GetCameraRay();
     Ray cameraRay = view3D_->GetCameraRay();
     float scale = gizmoNode_->GetScale().x_;
     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;
         cloning_ = true;
         selection_->Copy();
         selection_->Copy();
@@ -205,6 +203,7 @@ void Gizmo3D::Use()
             dragging_ = false;
             dragging_ = false;
         }
         }
 
 
+        startClone_ = false;
         cloning_ = false;
         cloning_ = false;
 
 
         CalculateGizmoAxes();
         CalculateGizmoAxes();
@@ -429,6 +428,16 @@ void Gizmo3D::Drag()
 
 
     float scale = gizmoNode_->GetScale().x_;
     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)
     if (editMode_ == EDIT_MOVE)
     {
     {
         Vector3 adjust(0, 0, 0);
         Vector3 adjust(0, 0, 0);

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

@@ -185,6 +185,7 @@ private:
 
 
     bool dragging_;
     bool dragging_;
     bool cloning_;
     bool cloning_;
+    bool startClone_;
 
 
     // snap settings
     // snap settings
     float snapTranslationX_;
     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)
 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);
     CefApp::OnBeforeCommandLineProcessing(process_type, command_line);
 }
 }
 
 

+ 17 - 8
Source/AtomicWebView/WebBrowserHost.cpp

@@ -97,6 +97,9 @@ private:
 
 
 GlobalPropertyMap WebBrowserHost::globalProperties_;
 GlobalPropertyMap WebBrowserHost::globalProperties_;
 WeakPtr<WebBrowserHost> WebBrowserHost::instance_;
 WeakPtr<WebBrowserHost> WebBrowserHost::instance_;
+String WebBrowserHost::userAgent_;
+String WebBrowserHost::productVersion_;
+int WebBrowserHost::debugPort_ = 3335;
 
 
 WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 {
 {
@@ -114,13 +117,10 @@ WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 #endif
 #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" };
     static const char* _argv[3] = { "AtomicWebView", "--disable-setuid-sandbox", "--off-screen-rendering-enabled" };
     CefMainArgs args(3, (char**) &_argv);
     CefMainArgs args(3, (char**) &_argv);
 #else
 #else
@@ -129,9 +129,18 @@ WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 
 
     CefSettings settings;
     CefSettings settings;
     settings.windowless_rendering_enabled = true;
     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);
     d_ = new WebBrowserHostPrivate(this);
 
 

+ 24 - 0
Source/AtomicWebView/WebBrowserHost.h

@@ -50,6 +50,25 @@ public:
 
 
     static const GlobalPropertyMap& GetGlobalProperties() { return globalProperties_; }
     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:
 private:
 
 
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
@@ -62,6 +81,11 @@ private:
 
 
     static GlobalPropertyMap globalProperties_;
     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
     CefRefPtr<CefRenderHandler> GetRenderHandler() OVERRIDE
     {
     {
 
 
-        if (webClient_->renderHandler_.Null())
+        if (webClient_.Null() || webClient_->renderHandler_.Null())
             return nullptr;
             return nullptr;
 
 
         return webClient_->renderHandler_->GetCEFRenderHandler();
         return webClient_->renderHandler_->GetCEFRenderHandler();
@@ -487,6 +487,14 @@ public:
 
 
     IMPLEMENT_REFCOUNTING(WebClientPrivate);
     IMPLEMENT_REFCOUNTING(WebClientPrivate);
 
 
+    void ClearReferences()
+    {
+        browser_ = nullptr;
+        webBrowserHost_ = nullptr;
+        webClient_ = nullptr;
+        browserSideRouter_ = nullptr;
+    }
+
 private:
 private:
 
 
     String initialLoadString_;
     String initialLoadString_;
@@ -503,6 +511,7 @@ private:
 WebClient::WebClient(Context* context) : Object(context)
 WebClient::WebClient(Context* context) : Object(context)
 {
 {
     d_ = new WebClientPrivate(this);
     d_ = new WebClientPrivate(this);
+    d_->AddRef();
 
 
     SubscribeToEvent(E_WEBVIEWGLOBALPROPERTIESCHANGED, HANDLER(WebClient, HandleWebViewGlobalPropertiesChanged));
     SubscribeToEvent(E_WEBVIEWGLOBALPROPERTIESCHANGED, HANDLER(WebClient, HandleWebViewGlobalPropertiesChanged));
 }
 }
@@ -520,10 +529,12 @@ WebClient::~WebClient()
         }
         }
 
 
         d_->CloseBrowser(true);
         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
 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.
 // THE SOFTWARE.
 //
 //
 
 
+#include <Atomic/Engine/Engine.h>
 #include <AtomicJS/Javascript/JSVM.h>
 #include <AtomicJS/Javascript/JSVM.h>
 
 
 #include "WebBrowserHost.h"
 #include "WebBrowserHost.h"
@@ -29,10 +30,23 @@ namespace Atomic
 
 
 extern void jsb_package_webview_init(JSVM* vm);
 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();
     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);
     jsb_package_webview_init(vm);
 
 
     duk_get_global_string(ctx, "WebView");
     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(SQLite)
     add_subdirectory(Poco)
     add_subdirectory(Poco)
     add_subdirectory(nativefiledialog)
     add_subdirectory(nativefiledialog)
+    add_subdirectory(libsquish)
 endif ()
 endif ()
 
 
 if (LINUX OR APPLE AND NOT IOS)
 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
 @interface SDLApplication : NSApplication
 
 
 - (void)terminate:(id)sender;
 - (void)terminate:(id)sender;
-
+- (void)sendEvent:(NSEvent *)theEvent;
 @end
 @end
 
 
 @implementation SDLApplication
 @implementation SDLApplication
@@ -50,6 +50,41 @@
     SDL_SendQuit();
     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
 @end // SDLApplication
 
 
 /* setAppleMenu disappeared from the headers in 10.4 */
 /* setAppleMenu disappeared from the headers in 10.4 */
@@ -316,28 +351,6 @@ Cocoa_PumpEvents(_THIS)
             break;
             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 */
         /* Pass through to NSApp to make sure everything stays in sync */
         [NSApp sendEvent:event];
         [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/ResourceEvents.h>
 #include <Atomic/Resource/ResourceCache.h>
 #include <Atomic/Resource/ResourceCache.h>
 
 
+#include "../Import/ImportConfig.h"
 #include "../ToolEvents.h"
 #include "../ToolEvents.h"
 #include "../ToolSystem.h"
 #include "../ToolSystem.h"
 #include "../Project/Project.h"
 #include "../Project/Project.h"
@@ -102,6 +103,24 @@ void AssetDatabase::RegisterGUID(const String& guid)
     usedGUID_.Push(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)
 void AssetDatabase::Import(const String& path)
 {
 {
     FileSystem* fs = GetSubsystem<FileSystem>();
     FileSystem* fs = GetSubsystem<FileSystem>();
@@ -434,6 +453,8 @@ void AssetDatabase::HandleProjectLoaded(StringHash eventType, VariantMap& eventD
 {
 {
     project_ = GetSubsystem<ToolSystem>()->GetProject();
     project_ = GetSubsystem<ToolSystem>()->GetProject();
 
 
+    ReadImportConfig();
+
     FileSystem* fs = GetSubsystem<FileSystem>();
     FileSystem* fs = GetSubsystem<FileSystem>();
 
 
     if (!fs->DirExists(GetCachePath()))
     if (!fs->DirExists(GetCachePath()))

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

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

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

@@ -152,15 +152,7 @@ void PrefabImporter::HandlePrefabSave(StringHash eventType, VariantMap& eventDat
     FileSystem* fs = GetSubsystem<FileSystem>();
     FileSystem* fs = GetSubsystem<FileSystem>();
     fs->Copy(asset_->GetPath(), asset_->GetCachePath());
     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());
     fs->Copy(asset_->GetPath(), asset_->GetCachePath());
 
 
+    OnPrefabFileChanged();
+
     return true;
     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)
 bool PrefabImporter::LoadSettingsInternal(JSONValue& jsonRoot)
 {
 {
     if (!AssetImporter::LoadSettingsInternal(jsonRoot))
     if (!AssetImporter::LoadSettingsInternal(jsonRoot))

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

@@ -55,6 +55,9 @@ protected:
     virtual bool LoadSettingsInternal(JSONValue& jsonRoot);
     virtual bool LoadSettingsInternal(JSONValue& jsonRoot);
     virtual bool SaveSettingsInternal(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:
 private:
 
 
     void HandlePrefabSave(StringHash eventType, VariantMap& eventData);
     void HandlePrefabSave(StringHash eventType, VariantMap& eventData);

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

@@ -24,6 +24,9 @@
 #include <Atomic/Resource/Image.h>
 #include <Atomic/Resource/Image.h>
 #include <Atomic/Atomic2D/Sprite2D.h>
 #include <Atomic/Atomic2D/Sprite2D.h>
 #include <Atomic/Atomic2D/StaticSprite2D.h>
 #include <Atomic/Atomic2D/StaticSprite2D.h>
+#include <Atomic/IO/FileSystem.h>
+
+#include <ToolCore/Import/ImportConfig.h>
 
 
 #include "Asset.h"
 #include "Asset.h"
 #include "AssetDatabase.h"
 #include "AssetDatabase.h"
@@ -32,9 +35,10 @@
 namespace ToolCore
 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()
 TextureImporter::~TextureImporter()
@@ -50,18 +54,29 @@ void TextureImporter::SetDefaults()
 bool TextureImporter::Import()
 bool TextureImporter::Import()
 {
 {
     AssetDatabase* db = GetSubsystem<AssetDatabase>();
     AssetDatabase* db = GetSubsystem<AssetDatabase>();
-
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     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());
     SharedPtr<Image> image = cache->GetTempResource<Image>(asset_->GetPath());
 
 
     if (image.Null())
     if (image.Null())
         return false;
         return false;
 
 
+    if (compressTextures_ &&
+        !image->IsCompressed())
+    {
+        fileSystem->CreateDirs(cachePath, "DDS/" + Atomic::GetPath(asset_->GetRelativePath()));
+        image->SaveDDS(compressedPath);
+    }
+
     // todo, proper proportions
     // todo, proper proportions
     image->Resize(64, 64);
     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
     // not sure entirely what we want to do here, though if the cache file doesn't exist
     // will reimport
     // will reimport
     image->SavePNG(cachePath + asset_->GetGUID());
     image->SavePNG(cachePath + asset_->GetGUID());
@@ -72,6 +87,22 @@ bool TextureImporter::Import()
     return true;
     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)
 bool TextureImporter::LoadSettingsInternal(JSONValue& jsonRoot)
 {
 {
     if (!AssetImporter::LoadSettingsInternal(jsonRoot))
     if (!AssetImporter::LoadSettingsInternal(jsonRoot))

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

@@ -44,10 +44,12 @@ public:
 protected:
 protected:
 
 
     bool Import();
     bool Import();
+    void ApplyProjectImportConfig();
 
 
     virtual bool LoadSettingsInternal(JSONValue& jsonRoot);
     virtual bool LoadSettingsInternal(JSONValue& jsonRoot);
     virtual bool SaveSettingsInternal(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;
     return true;
 }
 }
 
 
+bool BuildBase::CheckIncludeResourceFile(const String & resourceDir, const String & fileName)
+{
+    return (GetExtension(fileName) != ".psd");
+}
+
 bool BuildBase::BuildRemoveDirectory(const String& path)
 bool BuildBase::BuildRemoveDirectory(const String& path)
 {
 {
     if (buildFailed_)
     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;
             continue;
 
 
         BuildResourceEntry* newEntry = new BuildResourceEntry;
         BuildResourceEntry* newEntry = new BuildResourceEntry;

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

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

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

@@ -26,6 +26,7 @@
 #include "../ToolSystem.h"
 #include "../ToolSystem.h"
 #include "../ToolEnvironment.h"
 #include "../ToolEnvironment.h"
 #include "../Project/Project.h"
 #include "../Project/Project.h"
+#include "../Assets/AssetDatabase.h"
 
 
 #include "BuildEvents.h"
 #include "BuildEvents.h"
 #include "BuildSystem.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)
 void BuildMac::Build(const String& buildPath)
 {
 {
     ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
     ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();

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

@@ -45,6 +45,7 @@ public:
 protected:
 protected:
 
 
     void Initialize();
     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 "../ToolSystem.h"
 #include "../ToolEnvironment.h"
 #include "../ToolEnvironment.h"
 #include "../Project/Project.h"
 #include "../Project/Project.h"
+#include "../Assets/AssetDatabase.h"
 
 
 #include "BuildWindows.h"
 #include "BuildWindows.h"
 #include "BuildSystem.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()
 void BuildWindows::BuildAtomicNET()
 {
 {
     // AtomicNET
     // AtomicNET

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

@@ -45,6 +45,7 @@ public:
 protected:
 protected:
 
 
     void Initialize();
     void Initialize();
+    virtual bool CheckIncludeResourceFile(const String& resourceDir, const String& fileName);
 
 
 private:
 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/VertexBuffer.h>
 #include <Atomic/Graphics/Material.h>
 #include <Atomic/Graphics/Material.h>
 
 
+#include <ToolCore/Project/Project.h>
+#include <ToolCore/ToolSystem.h>
+#include <ToolCore/Import/ImportConfig.h>
+
 #include "OpenAssetImporter.h"
 #include "OpenAssetImporter.h"
 
 
 namespace ToolCore
 namespace ToolCore
@@ -86,6 +90,8 @@ OpenAssetImporter::OpenAssetImporter(Context* context) : Object(context) ,
         aiProcess_FindInstances |
         aiProcess_FindInstances |
         aiProcess_OptimizeMeshes;
         aiProcess_OptimizeMeshes;
 
 
+    ApplyProjectImportConfig();
+
     // TODO:  make this an option on importer
     // TODO:  make this an option on importer
 
 
     aiFlagsDefault_ |= aiProcess_CalcTangentSpace;
     aiFlagsDefault_ |= aiProcess_CalcTangentSpace;
@@ -860,6 +866,58 @@ void OpenAssetImporter::CollectAnimations(OutModel* model)
     /// \todo Vertex morphs are ignored for now
     /// \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)
 void OpenAssetImporter::BuildBoneCollisionInfo(OutModel& model)
 {
 {
     for (unsigned i = 0; i < model.meshes_.Size(); ++i)
     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 BuildBoneCollisionInfo(OutModel& model);
     void CollectAnimations(OutModel* model = 0);
     void CollectAnimations(OutModel* model = 0);
 
 
+    void ApplyProjectImportConfig();
+    void SetOveriddenFlags(VariantMap& aiFlagParameters);
+    void ApplyFlag(int processStep, bool active);
+
     String GetMeshMaterialName(aiMesh* mesh);
     String GetMeshMaterialName(aiMesh* mesh);
     String GenerateMaterialName(aiMaterial* material);
     String GenerateMaterialName(aiMaterial* material);
     String GetMaterialTextureName(const String& nameIn);
     String GetMaterialTextureName(const String& nameIn);