Browse Source

Atomic Glow Work

Josh Engebretson 8 years ago
parent
commit
9a2ec004ea
100 changed files with 13247 additions and 71 deletions
  1. 10 3
      Resources/CoreData/Shaders/GLSL/LitSolid.glsl
  2. 7 0
      Resources/CoreData/Shaders/GLSL/Transform.glsl
  3. 3 0
      Resources/CoreData/Shaders/GLSL/Uniforms.glsl
  4. 9 3
      Resources/CoreData/Shaders/HLSL/LitSolid.hlsl
  5. 7 0
      Resources/CoreData/Shaders/HLSL/Transform.hlsl
  6. 3 0
      Resources/CoreData/Shaders/HLSL/Uniforms.hlsl
  7. 9 0
      Resources/EditorData/AtomicEditor/editor/ui/atomicglowoutput.tb.txt
  8. 1 1
      Resources/EditorData/AtomicEditor/editor/ui/colorchooser.tb.txt
  9. 1 1
      Script/AtomicEditor/ui/frames/inspector/CreateComponentButton.ts
  10. 14 0
      Script/AtomicEditor/ui/frames/inspector/SelectionInspector.ts
  11. 1 1
      Script/AtomicEditor/ui/frames/inspector/SelectionSection.ts
  12. 52 4
      Script/AtomicEditor/ui/frames/inspector/SelectionSectionCoreUI.ts
  13. 4 1
      Script/AtomicEditor/ui/frames/inspector/SelectionSectionUI.ts
  14. 101 0
      Script/AtomicEditor/ui/modal/glow/GlowOutput.ts
  15. 5 4
      Script/Packages/Editor/Editor.json
  16. 14 0
      Source/Atomic/Engine/Engine.cpp
  17. 1 0
      Source/Atomic/Engine/EngineDefs.h
  18. 22 0
      Source/Atomic/Graphics/Batch.cpp
  19. 15 2
      Source/Atomic/Graphics/Batch.h
  20. 5 1
      Source/Atomic/Graphics/Drawable.cpp
  21. 6 0
      Source/Atomic/Graphics/Drawable.h
  22. 44 0
      Source/Atomic/Graphics/Graphics.cpp
  23. 2 0
      Source/Atomic/Graphics/Graphics.h
  24. 4 0
      Source/Atomic/Graphics/GraphicsDefs.cpp
  25. 4 0
      Source/Atomic/Graphics/GraphicsDefs.h
  26. 4 0
      Source/Atomic/Graphics/LMStaticModel.cpp
  27. 4 0
      Source/Atomic/Graphics/LMStaticModel.h
  28. 27 1
      Source/Atomic/Graphics/StaticModel.cpp
  29. 23 0
      Source/Atomic/Graphics/StaticModel.h
  30. 1 1
      Source/Atomic/IPC/IPC.h
  31. 12 0
      Source/Atomic/IPC/IPCEvents.h
  32. 8 1
      Source/Atomic/IPC/IPCServer.cpp
  33. 7 12
      Source/Atomic/IPC/IPCServer.h
  34. 16 0
      Source/Atomic/Math/Vector3.h
  35. 4 0
      Source/AtomicApp/IPCClientApp.h
  36. 15 1
      Source/AtomicEditor/Application/AEEditorApp.cpp
  37. 2 0
      Source/AtomicEditor/Components/EditorComponents.cpp
  38. 143 0
      Source/AtomicEditor/Components/GlowComponent.cpp
  39. 104 0
      Source/AtomicEditor/Components/GlowComponent.h
  40. 320 0
      Source/AtomicGlow/Atlas/MeshLightmapUVGen.cpp
  41. 71 0
      Source/AtomicGlow/Atlas/MeshLightmapUVGen.h
  42. 532 0
      Source/AtomicGlow/Atlas/ModelPacker.cpp
  43. 109 0
      Source/AtomicGlow/Atlas/ModelPacker.h
  44. 32 0
      Source/AtomicGlow/CMakeLists.txt
  45. 40 0
      Source/AtomicGlow/Common/GlowEvents.h
  46. 30 0
      Source/AtomicGlow/Common/GlowSettings.cpp
  47. 192 0
      Source/AtomicGlow/Common/GlowSettings.h
  48. 314 0
      Source/AtomicGlow/GlowApplication/AtomicGlowApp.cpp
  49. 63 0
      Source/AtomicGlow/GlowApplication/AtomicGlowApp.h
  50. 192 0
      Source/AtomicGlow/GlowService/GlowProcess.cpp
  51. 88 0
      Source/AtomicGlow/GlowService/GlowProcess.h
  52. 236 0
      Source/AtomicGlow/GlowService/GlowService.cpp
  53. 75 0
      Source/AtomicGlow/GlowService/GlowService.h
  54. 44 0
      Source/AtomicGlow/GlowService/GlowServiceEvents.h
  55. 477 0
      Source/AtomicGlow/Kernel/BakeLight.cpp
  56. 171 0
      Source/AtomicGlow/Kernel/BakeLight.h
  57. 167 0
      Source/AtomicGlow/Kernel/BakeMaterial.cpp
  58. 92 0
      Source/AtomicGlow/Kernel/BakeMaterial.h
  59. 745 0
      Source/AtomicGlow/Kernel/BakeMesh.cpp
  60. 220 0
      Source/AtomicGlow/Kernel/BakeMesh.h
  61. 97 0
      Source/AtomicGlow/Kernel/BakeModel.cpp
  62. 68 0
      Source/AtomicGlow/Kernel/BakeModel.h
  63. 41 0
      Source/AtomicGlow/Kernel/BakeNode.cpp
  64. 58 0
      Source/AtomicGlow/Kernel/BakeNode.h
  65. 26 0
      Source/AtomicGlow/Kernel/Embree.h
  66. 91 0
      Source/AtomicGlow/Kernel/EmbreeScene.cpp
  67. 61 0
      Source/AtomicGlow/Kernel/EmbreeScene.h
  68. 69 0
      Source/AtomicGlow/Kernel/GlowTypes.h
  69. 42 0
      Source/AtomicGlow/Kernel/LightMap.cpp
  70. 64 0
      Source/AtomicGlow/Kernel/LightMap.h
  71. 337 0
      Source/AtomicGlow/Kernel/LightMapPacker.cpp
  72. 75 0
      Source/AtomicGlow/Kernel/LightMapPacker.h
  73. 73 0
      Source/AtomicGlow/Kernel/LightRay.cpp
  74. 85 0
      Source/AtomicGlow/Kernel/LightRay.h
  75. 339 0
      Source/AtomicGlow/Kernel/RadianceMap.cpp
  76. 60 0
      Source/AtomicGlow/Kernel/RadianceMap.h
  77. 708 0
      Source/AtomicGlow/Kernel/Raster.cpp
  78. 28 0
      Source/AtomicGlow/Kernel/Raster.h
  79. 404 0
      Source/AtomicGlow/Kernel/SceneBaker.cpp
  80. 108 0
      Source/AtomicGlow/Kernel/SceneBaker.h
  81. 46 34
      Source/AtomicTool/AtomicTool.cpp
  82. 4 0
      Source/AtomicTool/CMakeLists.txt
  83. 5 0
      Source/CMakeLists.txt
  84. 407 0
      Source/ThirdParty/STB/stb_rect_pack.c
  85. 384 0
      Source/ThirdParty/embree/CHANGELOG.md
  86. 355 0
      Source/ThirdParty/embree/CMakeLists.txt
  87. 202 0
      Source/ThirdParty/embree/LICENSE.txt
  88. 3095 0
      Source/ThirdParty/embree/README.md
  89. 21 0
      Source/ThirdParty/embree/common/CMakeLists.txt
  90. 32 0
      Source/ThirdParty/embree/common/algorithms/CMakeLists.txt
  91. 67 0
      Source/ThirdParty/embree/common/algorithms/parallel_filter.cpp
  92. 103 0
      Source/ThirdParty/embree/common/algorithms/parallel_filter.h
  93. 61 0
      Source/ThirdParty/embree/common/algorithms/parallel_for.cpp
  94. 133 0
      Source/ThirdParty/embree/common/algorithms/parallel_for.h
  95. 76 0
      Source/ThirdParty/embree/common/algorithms/parallel_for_for.cpp
  96. 162 0
      Source/ThirdParty/embree/common/algorithms/parallel_for_for.h
  97. 98 0
      Source/ThirdParty/embree/common/algorithms/parallel_for_for_prefix_sum.cpp
  98. 80 0
      Source/ThirdParty/embree/common/algorithms/parallel_for_for_prefix_sum.h
  99. 60 0
      Source/ThirdParty/embree/common/algorithms/parallel_map.cpp
  100. 98 0
      Source/ThirdParty/embree/common/algorithms/parallel_map.h

+ 10 - 3
Resources/CoreData/Shaders/GLSL/LitSolid.glsl

@@ -86,7 +86,10 @@ void VS()
             // If using lightmap, disregard zone ambient light
             // If using lightmap, disregard zone ambient light
             // If using AO, calculate ambient in the PS
             // If using AO, calculate ambient in the PS
             vVertexLight = vec3(0.0, 0.0, 0.0);
             vVertexLight = vec3(0.0, 0.0, 0.0);
-            vTexCoord2 = iTexCoord1;
+            // ATOMIC BEGIN
+            // vTexCoord2 = iTexCoord1;
+            vTexCoord2 = GetLightMapTexCoord(iTexCoord1);
+            // ATOMIC END
         #else
         #else
             vVertexLight = GetAmbient(GetZonePos(worldPos));
             vVertexLight = GetAmbient(GetZonePos(worldPos));
         #endif
         #endif
@@ -199,7 +202,9 @@ void PS()
             finalColor += cMatEnvMapColor * textureCube(sEnvCubeMap, reflect(vReflectionVec, normal)).rgb;
             finalColor += cMatEnvMapColor * textureCube(sEnvCubeMap, reflect(vReflectionVec, normal)).rgb;
         #endif
         #endif
         #ifdef LIGHTMAP
         #ifdef LIGHTMAP
-            finalColor += texture2D(sEmissiveMap, vTexCoord2).rgb * diffColor.rgb;
+            // ATOMIC BEGIN
+            finalColor += texture2D(sEmissiveMap, vTexCoord2).rgb * vec3(4, 4, 4) * diffColor.rgb;
+            // ATOMIC END
         #endif
         #endif
         #ifdef EMISSIVEMAP
         #ifdef EMISSIVEMAP
             finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord.xy).rgb;
             finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord.xy).rgb;
@@ -232,7 +237,9 @@ void PS()
             finalColor += cMatEnvMapColor * textureCube(sEnvCubeMap, reflect(vReflectionVec, normal)).rgb;
             finalColor += cMatEnvMapColor * textureCube(sEnvCubeMap, reflect(vReflectionVec, normal)).rgb;
         #endif
         #endif
         #ifdef LIGHTMAP
         #ifdef LIGHTMAP
-            finalColor += texture2D(sEmissiveMap, vTexCoord2).rgb * diffColor.rgb;
+            // ATOMIC BEGIN
+            finalColor += texture2D(sEmissiveMap, vTexCoord2).rgb * vec3(4, 4, 4) * diffColor.rgb;
+            // ATOMIC END
         #endif
         #endif
         #ifdef EMISSIVEMAP
         #ifdef EMISSIVEMAP
             finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord.xy).rgb;
             finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord.xy).rgb;

+ 7 - 0
Resources/CoreData/Shaders/GLSL/Transform.glsl

@@ -53,6 +53,13 @@ vec2 GetTexCoord(vec2 texCoord)
     return vec2(dot(texCoord, cUOffset.xy) + cUOffset.w, dot(texCoord, cVOffset.xy) + cVOffset.w);
     return vec2(dot(texCoord, cUOffset.xy) + cUOffset.w, dot(texCoord, cVOffset.xy) + cVOffset.w);
 }
 }
 
 
+// ATOMIC BEGIN
+vec2 GetLightMapTexCoord(vec2 texCoord)
+{
+    return vec2(texCoord.x * cLMOffset.x + cLMOffset.z, texCoord.y * cLMOffset.y + cLMOffset.w);
+}
+// ATOMIC END
+
 vec4 GetClipPos(vec3 worldPos)
 vec4 GetClipPos(vec3 worldPos)
 {
 {
     vec4 ret = vec4(worldPos, 1.0) * cViewProj;
     vec4 ret = vec4(worldPos, 1.0) * cViewProj;

+ 3 - 0
Resources/CoreData/Shaders/GLSL/Uniforms.glsl

@@ -28,6 +28,9 @@ uniform mat4 cViewInv;
 uniform mat4 cViewProj;
 uniform mat4 cViewProj;
 uniform vec4 cUOffset;
 uniform vec4 cUOffset;
 uniform vec4 cVOffset;
 uniform vec4 cVOffset;
+// ATOMIC BEGIN
+uniform vec4 cLMOffset;
+// ATOMIC END
 uniform mat4 cZone;
 uniform mat4 cZone;
 #if !defined(GL_ES) || defined(WEBGL)
 #if !defined(GL_ES) || defined(WEBGL)
     uniform mat4 cLightMatrices[4];
     uniform mat4 cLightMatrices[4];

+ 9 - 3
Resources/CoreData/Shaders/HLSL/LitSolid.hlsl

@@ -118,7 +118,9 @@ void VS(float4 iPos : POSITION,
             // If using lightmap, disregard zone ambient light
             // If using lightmap, disregard zone ambient light
             // If using AO, calculate ambient in the PS
             // If using AO, calculate ambient in the PS
             oVertexLight = float3(0.0, 0.0, 0.0);
             oVertexLight = float3(0.0, 0.0, 0.0);
-            oTexCoord2 = iTexCoord2;
+            // ATOMIC BEGIN
+            oTexCoord2 = GetLightMapTexCoord(iTexCoord2);
+            // ATOMIC END
         #else
         #else
             oVertexLight = GetAmbient(GetZonePos(worldPos));
             oVertexLight = GetAmbient(GetZonePos(worldPos));
         #endif
         #endif
@@ -273,7 +275,9 @@ void PS(
             finalColor += cMatEnvMapColor * SampleCube(EnvCubeMap, reflect(iReflectionVec, normal)).rgb;
             finalColor += cMatEnvMapColor * SampleCube(EnvCubeMap, reflect(iReflectionVec, normal)).rgb;
         #endif
         #endif
         #ifdef LIGHTMAP
         #ifdef LIGHTMAP
-            finalColor += Sample2D(EmissiveMap, iTexCoord2).rgb * diffColor.rgb;
+            // ATOMIC BEGIN
+            finalColor += Sample2D(EmissiveMap, iTexCoord2).rgb * float3(4, 4, 4) * diffColor.rgb;
+            // ATOMIC END
         #endif
         #endif
         #ifdef EMISSIVEMAP
         #ifdef EMISSIVEMAP
             finalColor += cMatEmissiveColor * Sample2D(EmissiveMap, iTexCoord.xy).rgb;
             finalColor += cMatEmissiveColor * Sample2D(EmissiveMap, iTexCoord.xy).rgb;
@@ -306,7 +310,9 @@ void PS(
             finalColor += cMatEnvMapColor * SampleCube(EnvCubeMap, reflect(iReflectionVec, normal)).rgb;
             finalColor += cMatEnvMapColor * SampleCube(EnvCubeMap, reflect(iReflectionVec, normal)).rgb;
         #endif
         #endif
         #ifdef LIGHTMAP
         #ifdef LIGHTMAP
-            finalColor += Sample2D(EmissiveMap, iTexCoord2).rgb * diffColor.rgb;
+            // ATOMIC BEGIN
+            finalColor += Sample2D(EmissiveMap, iTexCoord2).rgb * float3(4, 4, 4) * diffColor.rgb;
+            // ATOMIC END
         #endif
         #endif
         #ifdef EMISSIVEMAP
         #ifdef EMISSIVEMAP
             finalColor += cMatEmissiveColor * Sample2D(EmissiveMap, iTexCoord.xy).rgb;
             finalColor += cMatEmissiveColor * Sample2D(EmissiveMap, iTexCoord.xy).rgb;

+ 7 - 0
Resources/CoreData/Shaders/HLSL/Transform.hlsl

@@ -21,6 +21,13 @@ float2 GetTexCoord(float2 iTexCoord)
     return float2(dot(iTexCoord, cUOffset.xy) + cUOffset.w, dot(iTexCoord, cVOffset.xy) + cVOffset.w);
     return float2(dot(iTexCoord, cUOffset.xy) + cUOffset.w, dot(iTexCoord, cVOffset.xy) + cVOffset.w);
 };
 };
 
 
+// ATOMIC BEGIN
+float2 GetLightMapTexCoord(float2 texCoord)
+{
+    return float2(texCoord.x * cLMOffset.x + cLMOffset.z, texCoord.y * cLMOffset.y + cLMOffset.w);
+}
+// ATOMIC END
+
 float4 GetClipPos(float3 worldPos)
 float4 GetClipPos(float3 worldPos)
 {
 {
     return mul(float4(worldPos, 1.0), cViewProj);
     return mul(float4(worldPos, 1.0), cViewProj);

+ 3 - 0
Resources/CoreData/Shaders/HLSL/Uniforms.hlsl

@@ -27,6 +27,9 @@ uniform float4x3 cViewInv;
 uniform float4x4 cViewProj;
 uniform float4x4 cViewProj;
 uniform float4 cUOffset;
 uniform float4 cUOffset;
 uniform float4 cVOffset;
 uniform float4 cVOffset;
+// ATOMIC BEGIN
+uniform float4 cLMOffset;
+// ATOMIC END
 uniform float4x3 cZone;
 uniform float4x3 cZone;
 #ifdef SKINNED
 #ifdef SKINNED
     uniform float4x3 cSkinMatrices[MAXBONES];
     uniform float4x3 cSkinMatrices[MAXBONES];

+ 9 - 0
Resources/EditorData/AtomicEditor/editor/ui/atomicglowoutput.tb.txt

@@ -0,0 +1,9 @@
+TBLayout: axis: y, distribution: gravity, position: left
+	TBTextField: text: "Output:"
+	TBEditField: multiline: 1, styling: 1, gravity: all, id: output, readonly: 1, adapt-to-content: 0
+		lp: min-width: 640, min-height: 480
+		font: size: 11
+	TBSeparator: gravity: left right, skin: AESeparator
+	TBLayout:
+		TBButton: text: OK, id: ok
+		TBButton: text: Cancel, id: cancel

+ 1 - 1
Resources/EditorData/AtomicEditor/editor/ui/colorchooser.tb.txt

@@ -8,7 +8,7 @@ TBLayout: axis: y, distribution: gravity, position: left
 	TBLayout: axis: x, distribution: gravity
 	TBLayout: axis: x, distribution: gravity
 		TBLayout: axis: x, distribution: gravity
 		TBLayout: axis: x, distribution: gravity
 			TBColorWheel: id: colorwheel, skin: HSVSkin
 			TBColorWheel: id: colorwheel, skin: HSVSkin
-					lp: width: 256, height: 256
+				lp: width: 256, height: 256
 			TBSlider: id: lslider, axis: y, min: 0, max: 255, value: 128
 			TBSlider: id: lslider, axis: y, min: 0, max: 255, value: 128
 
 
 		TBLayout: axis: y, distribution: gravity
 		TBLayout: axis: y, distribution: gravity

+ 1 - 1
Script/AtomicEditor/ui/frames/inspector/CreateComponentButton.ts

@@ -95,9 +95,9 @@ subsystemCreateSource.addItem(new Atomic.UIMenuItem("PhysicsWorld", "create comp
 
 
 var editorCreateSource = new Atomic.UIMenuItemSource();
 var editorCreateSource = new Atomic.UIMenuItemSource();
 
 
+editorCreateSource.addItem(new Atomic.UIMenuItem("GlowComponent", "GlowComponent"));
 editorCreateSource.addItem(new Atomic.UIMenuItem("CubemapGenerator", "CubemapGenerator"));
 editorCreateSource.addItem(new Atomic.UIMenuItem("CubemapGenerator", "CubemapGenerator"));
 
 
-
 var componentCreateSource = new Atomic.UIMenuItemSource();
 var componentCreateSource = new Atomic.UIMenuItemSource();
 
 
 var sources = {
 var sources = {

+ 14 - 0
Script/AtomicEditor/ui/frames/inspector/SelectionInspector.ts

@@ -470,6 +470,20 @@ class SelectionInspector extends ScriptWidget {
 
 
     }
     }
 
 
+    getSection(editType: SerializableEditType) {
+
+        for (var i in this.sections) {
+
+            var section = this.sections[i];
+            if (section.editType == editType)
+                return section;
+        
+        }
+
+        return null;
+
+    }    
+
     removeSection(section: SelectionSection) {
     removeSection(section: SelectionSection) {
 
 
         SelectionInspector.sectionStates[section.editType.typeName] = section.value ? true : false;
         SelectionInspector.sectionStates[section.editType.typeName] = section.value ? true : false;

+ 1 - 1
Script/AtomicEditor/ui/frames/inspector/SelectionSection.ts

@@ -177,7 +177,7 @@ abstract class SelectionSection extends Atomic.UISection {
         if (SelectionSection.customSectionUI[this.editType.typeName]) {
         if (SelectionSection.customSectionUI[this.editType.typeName]) {
 
 
             this.customUI = new SelectionSection.customSectionUI[this.editType.typeName]();
             this.customUI = new SelectionSection.customSectionUI[this.editType.typeName]();
-            this.customUI.createUI(this.editType);
+            this.customUI.createUI(this.editType, this);
             attrLayout.addChild(this.customUI);
             attrLayout.addChild(this.customUI);
 
 
         }
         }

+ 52 - 4
Script/AtomicEditor/ui/frames/inspector/SelectionSectionCoreUI.ts

@@ -28,12 +28,13 @@ import SerializableEditType = require("./SerializableEditType");
 
 
 import ProgressModal = require("ui/modal/ProgressModal");
 import ProgressModal = require("ui/modal/ProgressModal");
 
 
+import GlowOutput = require("ui/modal/glow/GlowOutput");
 
 
 class CollisionShapeSectionUI extends SelectionSectionUI {
 class CollisionShapeSectionUI extends SelectionSectionUI {
 
 
-    createUI(editType: SerializableEditType) {
+    createUI(editType: SerializableEditType, selectionSection: SelectionSection) {
 
 
-        this.editType = editType;
+        super.createUI(editType, selectionSection);
 
 
         var button = new Atomic.UIButton();
         var button = new Atomic.UIButton();
         button.fontDescription = InspectorUtils.attrFontDesc;
         button.fontDescription = InspectorUtils.attrFontDesc;
@@ -64,9 +65,9 @@ class CollisionShapeSectionUI extends SelectionSectionUI {
 
 
 class CubemapGeneratorSectionUI extends SelectionSectionUI {
 class CubemapGeneratorSectionUI extends SelectionSectionUI {
 
 
-    createUI(editType: SerializableEditType) {
+    createUI(editType: SerializableEditType, selectionSection: SelectionSection) {
 
 
-        this.editType = editType;
+        super.createUI(editType, selectionSection);
 
 
         var button = new Atomic.UIButton();
         var button = new Atomic.UIButton();
         button.fontDescription = InspectorUtils.attrFontDesc;
         button.fontDescription = InspectorUtils.attrFontDesc;
@@ -132,5 +133,52 @@ class CubemapGeneratorSectionUI extends SelectionSectionUI {
 
 
 }
 }
 
 
+class GlowComponentSectionUI extends SelectionSectionUI {
+
+    createUI(editType: SerializableEditType, selectionSection: SelectionSection) {
+
+        super.createUI(editType, selectionSection);
+
+        var button = new Atomic.UIButton();
+        button.fontDescription = InspectorUtils.attrFontDesc;
+        button.gravity = Atomic.UI_GRAVITY.UI_GRAVITY_RIGHT;
+        button.text = "Bake";
+
+        button.onClick = () => {
+
+            var glow = <Editor.GlowComponent>this.editType.objects[0];
+
+            var glowOutput:GlowOutput;
+
+            glow.subscribeToEvent(Editor.AtomicGlowBakeResultEvent((evData) => {
+
+                if (glowOutput) {
+                    glowOutput.hide();
+                    glowOutput.close();
+                    glowOutput = null;
+                }
+
+                glow.unsubscribeFromEvent(Editor.AtomicGlowBakeResultEventType);
+
+            }));
+
+            if (glow.bake()) {
+
+                this.selectionSection.refresh();
+                glowOutput = new GlowOutput();
+                glowOutput.show();
+            }
+
+
+        };
+
+        this.addChild(button);
+
+    }
+
+}
+
+
 SelectionSection.registerCustomSectionUI("CollisionShape", CollisionShapeSectionUI);
 SelectionSection.registerCustomSectionUI("CollisionShape", CollisionShapeSectionUI);
 SelectionSection.registerCustomSectionUI("CubemapGenerator", CubemapGeneratorSectionUI);
 SelectionSection.registerCustomSectionUI("CubemapGenerator", CubemapGeneratorSectionUI);
+SelectionSection.registerCustomSectionUI("GlowComponent", GlowComponentSectionUI);

+ 4 - 1
Script/AtomicEditor/ui/frames/inspector/SelectionSectionUI.ts

@@ -21,17 +21,20 @@
 //
 //
 
 
 import SerializableEditType = require("./SerializableEditType");
 import SerializableEditType = require("./SerializableEditType");
+import SelectionSection = require("./SelectionSection");
 
 
 class SelectionSectionUI extends Atomic.UILayout {
 class SelectionSectionUI extends Atomic.UILayout {
 
 
     editType: SerializableEditType;
     editType: SerializableEditType;
+    selectionSection: SelectionSection;
 
 
     refresh() {
     refresh() {
 
 
     }
     }
 
 
-    createUI(editType: SerializableEditType) {
+    createUI(editType: SerializableEditType, selectionSection: SelectionSection) {
 
 
+      this.selectionSection = selectionSection;
       this.editType = editType;
       this.editType = editType;
 
 
     }
     }

+ 101 - 0
Script/AtomicEditor/ui/modal/glow/GlowOutput.ts

@@ -0,0 +1,101 @@
+//
+// 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");
+
+
+class GlowOutput extends Atomic.UIWindow {
+
+    constructor() {
+
+        super();
+
+        this.settings = Atomic.UI_WINDOW_SETTINGS.UI_WINDOW_SETTINGS_DEFAULT & ~Atomic.UI_WINDOW_SETTINGS.UI_WINDOW_SETTINGS_CLOSE_BUTTON;
+
+        this.text = "Atomic Glow";
+        this.load("AtomicEditor/editor/ui/atomicglowoutput.tb.txt");
+
+        this.outputField = <Atomic.UIEditField>this.getWidget("output");
+
+        this.resizeToFitContent();
+        this.center();
+
+        this.subscribeToEvent(Editor.AtomicGlowLogEvent((ev: Editor.AtomicGlowLogEvent) => {
+
+            this.textOutput += ev.message + "\n";
+            this.outputField.text = this.textOutput;
+            this.outputField.scrollTo(0, 0xffffff);
+
+        }));
+
+        this.subscribeToEvent(this, Atomic.UIWidgetEvent((data) => this.handleWidgetEvent(data)));
+
+        this.dimmer = new Atomic.UIDimmer();
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent): boolean {
+
+        if (ev.type == Atomic.UI_EVENT_TYPE.UI_EVENT_TYPE_CLICK) {
+
+            if (ev.target.id == "cancel") {
+                this.sendEvent(Editor.AtomicGlowBakeCancelEventType);
+                return true;
+            }
+
+            if (ev.target.id == "ok") {
+                return true;
+            }
+
+        }
+
+        return false;
+    }
+
+    show() {
+
+        var view = EditorUI.getView();
+        view.addChild(this.dimmer);
+        view.addChild(this);
+
+    }
+
+    hide() {
+
+        this.unsubscribeFromEvent(Editor.AtomicGlowLogEventType);
+
+        if (this.dimmer.parent)
+            this.dimmer.parent.removeChild(this.dimmer, false);
+
+        if (this.parent)
+            this.parent.removeChild(this, false);
+
+    }
+
+    dimmer: Atomic.UIDimmer;
+    
+    textOutput: string = "";
+    outputField: Atomic.UIEditField;
+
+}
+
+export = GlowOutput;

+ 5 - 4
Script/Packages/Editor/Editor.json

@@ -2,9 +2,10 @@
 	"name" : "Editor",
 	"name" : "Editor",
 	"includes" : ["<Atomic/Graphics/DebugRenderer.h>", "<AtomicWebView/UIWebView.h>"],
 	"includes" : ["<Atomic/Graphics/DebugRenderer.h>", "<AtomicWebView/UIWebView.h>"],
 	"sources" : ["Source/AtomicEditor/Application", "Source/AtomicEditor/Utils",
 	"sources" : ["Source/AtomicEditor/Application", "Source/AtomicEditor/Utils",
-							 "Source/AtomicEditor/EditorMode", "Source/AtomicEditor/PlayerMode",
-							 "Source/AtomicEditor/Editors", "Source/AtomicEditor/Editors/SceneEditor3D",
-						   "Source/AtomicEditor/Components"],
+				 "Source/AtomicEditor/EditorMode", "Source/AtomicEditor/PlayerMode",
+				 "Source/AtomicEditor/Editors", "Source/AtomicEditor/Editors/SceneEditor3D",
+				 "Source/AtomicEditor/Components"],
 	"classes" : ["EditorMode", "PlayerMode", "FileUtils", "ResourceEditor", "JSResourceEditor",
 	"classes" : ["EditorMode", "PlayerMode", "FileUtils", "ResourceEditor", "JSResourceEditor",
-							 "SceneEditor3D", "SceneView3D", "SceneSelection", "EditorComponent", "CubemapGenerator", "Gizmo3D"]
+				 "SceneEditor3D", "SceneView3D", "SceneSelection", "EditorComponent", "CubemapGenerator", "Gizmo3D",
+			 	 "GlowComponent"]
 }
 }

+ 14 - 0
Source/Atomic/Engine/Engine.cpp

@@ -236,6 +236,15 @@ bool Engine::Initialize(const VariantMap& parameters)
     // unpredictable extra synchronization overhead. Also reserve one core for the main thread
     // unpredictable extra synchronization overhead. Also reserve one core for the main thread
 #ifdef ATOMIC_THREADING
 #ifdef ATOMIC_THREADING
     unsigned numThreads = GetParameter(parameters, EP_WORKER_THREADS, true).GetBool() ? GetNumPhysicalCPUs() - 1 : 0;
     unsigned numThreads = GetParameter(parameters, EP_WORKER_THREADS, true).GetBool() ? GetNumPhysicalCPUs() - 1 : 0;
+
+    // ATOMIC BEGIN
+    if (numThreads)
+    {
+        // check for explicitly set worker thread count
+        numThreads = GetParameter(parameters, EP_WORKER_THREADS_COUNT, numThreads).GetUInt();
+    }
+    // ATOMIC END
+
     if (numThreads)
     if (numThreads)
     {
     {
         GetSubsystem<WorkQueue>()->CreateThreads(numThreads);
         GetSubsystem<WorkQueue>()->CreateThreads(numThreads);
@@ -998,6 +1007,11 @@ VariantMap Engine::ParseParameters(const Vector<String>& arguments)
             else if (argument == "-autometrics") // --autometrics
             else if (argument == "-autometrics") // --autometrics
             {
             {
                 ret[EP_AUTO_METRICS] = true;
                 ret[EP_AUTO_METRICS] = true;
+            }            
+            else if (argument == "workerthreadcount" && !value.Empty())
+            {
+                ret[EP_WORKER_THREADS_COUNT] = ToInt(value);
+                ++i;
             }
             }
             // ATOMIC END
             // ATOMIC END
 #ifdef ATOMIC_TESTING
 #ifdef ATOMIC_TESTING

+ 1 - 0
Source/Atomic/Engine/EngineDefs.h

@@ -76,5 +76,6 @@ static const String EP_WORKER_THREADS = "WorkerThreads";
 // ATOMIC BEGIN
 // ATOMIC BEGIN
 static const String EP_WINDOW_MAXIMIZED = "WindowMaximized";
 static const String EP_WINDOW_MAXIMIZED = "WindowMaximized";
 static const String EP_AUTO_METRICS = "AutoMetrics";
 static const String EP_AUTO_METRICS = "AutoMetrics";
+static const String EP_WORKER_THREADS_COUNT = "WorkerThreadsCount";
 // ATOMIC END
 // ATOMIC END
 }
 }

+ 22 - 0
Source/Atomic/Graphics/Batch.cpp

@@ -260,6 +260,13 @@ void Batch::Prepare(View* view, Camera* camera, bool setModelTransform, bool all
         }
         }
     }
     }
 
 
+    // ATOMIC BEGIN
+    if (lightmapTilingOffset_)
+    {
+        graphics->SetShaderParameter(VSP_LMOFFSET, *lightmapTilingOffset_);
+    }
+    // ATOMIC END
+
     // Set zone-related shader parameters
     // Set zone-related shader parameters
     BlendMode blend = graphics->GetBlendMode();
     BlendMode blend = graphics->GetBlendMode();
     // If the pass is additive, override fog color to black so that shaders do not need a separate additive path
     // If the pass is additive, override fog color to black so that shaders do not need a separate additive path
@@ -603,12 +610,27 @@ void Batch::Prepare(View* view, Camera* camera, bool setModelTransform, bool all
                 graphics->SetShaderParameter(i->first_, i->second_.value_);
                 graphics->SetShaderParameter(i->first_, i->second_.value_);
         }
         }
 
 
+        // ATOMIC BEGIN
+
         const HashMap<TextureUnit, SharedPtr<Texture> >& textures = material_->GetTextures();
         const HashMap<TextureUnit, SharedPtr<Texture> >& textures = material_->GetTextures();
         for (HashMap<TextureUnit, SharedPtr<Texture> >::ConstIterator i = textures.Begin(); i != textures.End(); ++i)
         for (HashMap<TextureUnit, SharedPtr<Texture> >::ConstIterator i = textures.Begin(); i != textures.End(); ++i)
         {
         {
             if (graphics->HasTextureUnit(i->first_))
             if (graphics->HasTextureUnit(i->first_))
+            {
+                if (i->first_ == TU_EMISSIVE && lightmapTilingOffset_)
+                    continue;
+
                 graphics->SetTexture(i->first_, i->second_.Get());
                 graphics->SetTexture(i->first_, i->second_.Get());
+            }
+        }
+
+        if (lightmapTilingOffset_)
+        {
+            graphics->SetLightmapTexture(lightmapTextureID_);
         }
         }
+
+        // ATOMIC END
+
     }
     }
 
 
     // Set light-related textures
     // Set light-related textures

+ 15 - 2
Source/Atomic/Graphics/Batch.h

@@ -52,7 +52,11 @@ struct Batch
     /// Construct with defaults.
     /// Construct with defaults.
     Batch() :
     Batch() :
         isBase_(false),
         isBase_(false),
-        lightQueue_(0)
+        lightQueue_(0),
+        // ATOMIC BEGIN
+        lightmapTilingOffset_(0),
+        lightmapTextureID_(0)
+        // ATOMIC END
     {
     {
     }
     }
 
 
@@ -67,7 +71,11 @@ struct Batch
         numWorldTransforms_(rhs.numWorldTransforms_),
         numWorldTransforms_(rhs.numWorldTransforms_),
         instancingData_(rhs.instancingData_),
         instancingData_(rhs.instancingData_),
         lightQueue_(0),
         lightQueue_(0),
-        geometryType_(rhs.geometryType_)
+        geometryType_(rhs.geometryType_),
+        // ATOMIC BEGIN        
+        lightmapTilingOffset_(rhs.lightmapTilingOffset_),
+        lightmapTextureID_(rhs.lightmapTextureID_)
+        // ATOMIC END
     {
     {
     }
     }
 
 
@@ -110,6 +118,11 @@ struct Batch
     ShaderVariation* pixelShader_;
     ShaderVariation* pixelShader_;
     /// %Geometry type.
     /// %Geometry type.
     GeometryType geometryType_;
     GeometryType geometryType_;
+
+    // ATOMIC BEGIN
+     Vector4* lightmapTilingOffset_;
+     unsigned lightmapTextureID_;
+    // ATOMIC END
 };
 };
 
 
 /// Data for one geometry instance.
 /// Data for one geometry instance.

+ 5 - 1
Source/Atomic/Graphics/Drawable.cpp

@@ -57,7 +57,11 @@ SourceBatch::SourceBatch() :
     worldTransform_(&Matrix3x4::IDENTITY),
     worldTransform_(&Matrix3x4::IDENTITY),
     numWorldTransforms_(1),
     numWorldTransforms_(1),
     instancingData_((void*)0),
     instancingData_((void*)0),
-    geometryType_(GEOM_STATIC)
+    geometryType_(GEOM_STATIC),
+    // ATOMIC BEGIN
+    lightmapTilingOffset_(0),
+    lightmapTextureID_(0)
+    // ATOMIC END
 {
 {
 }
 }
 
 

+ 6 - 0
Source/Atomic/Graphics/Drawable.h

@@ -104,6 +104,12 @@ struct ATOMIC_API SourceBatch
     void* instancingData_;
     void* instancingData_;
     /// %Geometry type.
     /// %Geometry type.
     GeometryType geometryType_;
     GeometryType geometryType_;
+
+    // ATOMIC BEGIN
+    /// Lightmap Tiling Offset
+    Vector4* lightmapTilingOffset_;
+    unsigned lightmapTextureID_;
+    // ATOMIC END
 };
 };
 
 
 /// Base class for visible components.
 /// Base class for visible components.

+ 44 - 0
Source/Atomic/Graphics/Graphics.cpp

@@ -52,6 +52,9 @@
 #include "../IO/FileSystem.h"
 #include "../IO/FileSystem.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 
 
+// FIXME: Hack
+#include "../Resource/ResourceCache.h"
+
 // ATOMIC BEGIN
 // ATOMIC BEGIN
 
 
 #include "Text3D/Text3DFont.h"
 #include "Text3D/Text3DFont.h"
@@ -447,6 +450,47 @@ void Graphics::RaiseWindow()
         SDL_RaiseWindow(window_);
         SDL_RaiseWindow(window_);
 }
 }
 
 
+// FIXME: Hack
+#include "./Resource/ResourceCache.h"
+static PODVector<Texture2D*> lightmapTextures;
+
+void Graphics::SetLightmapTexture(unsigned id)
+{
+    const unsigned numLightmaps = 25;
+    static bool initialized = false;
+
+    if (!lightmapTextures.Size() && !initialized)
+    {
+        initialized = true;
+
+        ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+        for (unsigned i = 0; i < numLightmaps; i++)
+        {
+            Texture2D* texture = cache->GetResource<Texture2D>(ToString("Textures/Scene_Lightmap%u.png", i));
+
+            if (!texture)
+                break;
+
+            // FILTER_NEAREST is good for testing lightmap, without bilinear artifacts
+            // texture->SetFilterMode(FILTER_NEAREST);
+            texture->SetNumLevels(1); // No mipmaps
+
+            texture->SetAddressMode(COORD_U, ADDRESS_CLAMP);
+            texture->SetAddressMode(COORD_V, ADDRESS_CLAMP);
+
+            lightmapTextures.Push(texture);
+
+        }
+
+    }
+
+    if (id >= lightmapTextures.Size())
+        return;
+
+    SetTexture(TU_EMISSIVE, lightmapTextures[id]);
+
+}
 
 
 // ATOMIC END
 // ATOMIC END
 
 

+ 2 - 0
Source/Atomic/Graphics/Graphics.h

@@ -582,6 +582,8 @@ public:
     static unsigned GetSinglePassPrimitives() { return numSinglePassPrimitives_; }
     static unsigned GetSinglePassPrimitives() { return numSinglePassPrimitives_; }
     /// Set number of single render pass primitives drawn this frame (D3D9 Only)
     /// Set number of single render pass primitives drawn this frame (D3D9 Only)
     static void SetSinglePassPrimitives(unsigned value) { numSinglePassPrimitives_ = value; }
     static void SetSinglePassPrimitives(unsigned value) { numSinglePassPrimitives_ = value; }
+
+    void SetLightmapTexture(unsigned id);
   
   
     // ATOMIC END
     // ATOMIC END
 
 

+ 4 - 0
Source/Atomic/Graphics/GraphicsDefs.cpp

@@ -90,6 +90,10 @@ extern ATOMIC_API const StringHash PSP_LIGHTLENGTH("LightLength");
 extern ATOMIC_API const StringHash PSP_ZONEMIN("ZoneMin");
 extern ATOMIC_API const StringHash PSP_ZONEMIN("ZoneMin");
 extern ATOMIC_API const StringHash PSP_ZONEMAX("ZoneMax");
 extern ATOMIC_API const StringHash PSP_ZONEMAX("ZoneMax");
 
 
+// ATOMIC BEGIN
+extern ATOMIC_API const StringHash VSP_LMOFFSET("LMOffset");
+// ATOMIC END
+
 extern ATOMIC_API const Vector3 DOT_SCALE(1 / 3.0f, 1 / 3.0f, 1 / 3.0f);
 extern ATOMIC_API const Vector3 DOT_SCALE(1 / 3.0f, 1 / 3.0f, 1 / 3.0f);
 
 
 extern ATOMIC_API const VertexElement LEGACY_VERTEXELEMENTS[] =
 extern ATOMIC_API const VertexElement LEGACY_VERTEXELEMENTS[] =

+ 4 - 0
Source/Atomic/Graphics/GraphicsDefs.h

@@ -427,6 +427,10 @@ extern ATOMIC_API const StringHash PSP_LIGHTLENGTH;
 extern ATOMIC_API const StringHash PSP_ZONEMIN;
 extern ATOMIC_API const StringHash PSP_ZONEMIN;
 extern ATOMIC_API const StringHash PSP_ZONEMAX;
 extern ATOMIC_API const StringHash PSP_ZONEMAX;
 
 
+// ATOMIC BEGIN
+extern ATOMIC_API const StringHash VSP_LMOFFSET;
+// ATOMIC END
+
 // Scale calculation from bounding box diagonal.
 // Scale calculation from bounding box diagonal.
 extern ATOMIC_API const Vector3 DOT_SCALE;
 extern ATOMIC_API const Vector3 DOT_SCALE;
 
 

+ 4 - 0
Source/Atomic/Graphics/LMStaticModel.cpp

@@ -8,6 +8,8 @@
 #include "../Graphics/Technique.h"
 #include "../Graphics/Technique.h"
 #include "LMStaticModel.h"
 #include "LMStaticModel.h"
 
 
+#ifdef __DISABLED
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 
@@ -103,4 +105,6 @@ void LMStaticModel::UpdateBatches(const FrameInfo& frame)
 
 
 }
 }
 
 
+#endif
+
 
 

+ 4 - 0
Source/Atomic/Graphics/LMStaticModel.h

@@ -8,6 +8,8 @@
 #include "../Graphics/Texture2D.h"
 #include "../Graphics/Texture2D.h"
 #include "../Graphics/Material.h"
 #include "../Graphics/Material.h"
 
 
+#ifdef __DISABLED
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 
@@ -44,3 +46,5 @@ private:
 };
 };
 
 
 }
 }
+
+#endif

+ 27 - 1
Source/Atomic/Graphics/StaticModel.cpp

@@ -47,7 +47,14 @@ extern const char* GEOMETRY_CATEGORY;
 StaticModel::StaticModel(Context* context) :
 StaticModel::StaticModel(Context* context) :
     Drawable(context, DRAWABLE_GEOMETRY),
     Drawable(context, DRAWABLE_GEOMETRY),
     occlusionLodLevel_(M_MAX_UNSIGNED),
     occlusionLodLevel_(M_MAX_UNSIGNED),
-    materialsAttr_(Material::GetTypeStatic())
+    materialsAttr_(Material::GetTypeStatic()),
+    // ATOMIC BEGIN
+    lightmap_(false),
+    lightmapScale_(1.0f),
+    lightmapSize_(0),
+    lightmapIndex_(0),
+    lightmapTilingOffset_(1.0f, 1.0f, 0.0f, 0.0f)
+    // ATOMIC END
 {
 {
 }
 }
 
 
@@ -77,6 +84,13 @@ void StaticModel::RegisterObject(Context* context)
     ATOMIC_ACCESSOR_ATTRIBUTE("Geometry Enabled", GetGeometryEnabledAttr, SetGeometryEnabledAttr, VariantVector,
     ATOMIC_ACCESSOR_ATTRIBUTE("Geometry Enabled", GetGeometryEnabledAttr, SetGeometryEnabledAttr, VariantVector,
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
 
 
+    ATOMIC_ATTRIBUTE("Lightmap", bool, lightmap_, false, AM_DEFAULT);
+    ATOMIC_ATTRIBUTE("Lightmap Scale", float, lightmapScale_, 1.0f, AM_DEFAULT);
+    ATOMIC_ATTRIBUTE("Lightmap Size", unsigned, lightmapSize_, 0, AM_DEFAULT);
+
+    ATOMIC_ATTRIBUTE("Lightmap Index", unsigned, lightmapIndex_, 0, AM_FILE | AM_NOEDIT);
+    ATOMIC_ATTRIBUTE("Lightmap Tiling Offset", Vector4, lightmapTilingOffset_ , Vector4(1.0f, 1.0f, 0.0f, 0.0f), AM_FILE | AM_NOEDIT);
+
     // ATOMIC END
     // ATOMIC END
 
 
 }
 }
@@ -164,7 +178,19 @@ void StaticModel::UpdateBatches(const FrameInfo& frame)
 
 
     // ATOMIC BEGIN
     // ATOMIC BEGIN
     if (geometryDisabled_)
     if (geometryDisabled_)
+    {
         UpdateBatchesHideGeometry();
         UpdateBatchesHideGeometry();
+    }
+
+    if (lightmap_)
+    {
+        for (unsigned i = 0; i < batches_.Size(); ++i)
+        {
+            batches_[i].lightmapTextureID_ = lightmapIndex_;
+            batches_[i].geometryType_ = GEOM_STATIC_NOINSTANCING;
+            batches_[i].lightmapTilingOffset_ = &lightmapTilingOffset_;
+        }
+    }
     // ATOMIC END
     // ATOMIC END
 
 
 }
 }

+ 23 - 0
Source/Atomic/Graphics/StaticModel.h

@@ -122,6 +122,21 @@ public:
     void SetGeometryEnabledAttr(const VariantVector& value);
     void SetGeometryEnabledAttr(const VariantVector& value);
     const VariantVector& GetGeometryEnabledAttr() const;
     const VariantVector& GetGeometryEnabledAttr() const;
 
 
+    bool GetLightmap() const { return lightmap_; }
+    void SetLightmap(bool lightmap) { lightmap_ = lightmap; }
+
+    float GetLightmapScale() const { return lightmapScale_; }
+    void SetLightmapScale(float scale) { lightmapScale_ = scale; }
+
+    unsigned GetLightmapSize() const { return lightmapSize_; }
+    void SetLightmapSize(unsigned size) { lightmapSize_ = size; }
+
+    unsigned GetLightmapIndex() const { return lightmapIndex_; }
+    void SetLightmapIndex(unsigned idx) { lightmapIndex_ = idx; }
+
+    const Vector4& GetLightmapTilingOffset() const { return lightmapTilingOffset_; }
+    void SetLightmapTilingOffset(Vector4 tilingOffset) { lightmapTilingOffset_ = tilingOffset; }
+
     // ATOMIC END
     // ATOMIC END
 
 
 protected:
 protected:
@@ -155,6 +170,14 @@ protected:
     mutable VariantVector geometryEnabled_;
     mutable VariantVector geometryEnabled_;
     /// true if any geometry has been disabled
     /// true if any geometry has been disabled
     mutable bool geometryDisabled_;
     mutable bool geometryDisabled_;
+
+    bool lightmap_;
+    float lightmapScale_;
+    unsigned lightmapSize_;
+
+    unsigned lightmapIndex_;
+    Vector4 lightmapTilingOffset_;
+
     // ATOMIC END
     // ATOMIC END
 
 
 private:
 private:

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

@@ -89,7 +89,7 @@ private:
 
 
     Vector<SharedPtr<IPCBroker> > brokers_;
     Vector<SharedPtr<IPCBroker> > brokers_;
 
 
-    // valid on child
+    // valid on child process
     SharedPtr<IPCWorker> worker_;
     SharedPtr<IPCWorker> worker_;
 
 
 #ifdef ATOMIC_PLATFORM_WINDOWS
 #ifdef ATOMIC_PLATFORM_WINDOWS

+ 12 - 0
Source/Atomic/IPC/IPCEvents.h

@@ -60,5 +60,17 @@ ATOMIC_EVENT(E_IPCMESSAGE, IPCMessage)
     ATOMIC_PARAM(P_VALUE, Value);  // int
     ATOMIC_PARAM(P_VALUE, Value);  // int
 }
 }
 
 
+ATOMIC_EVENT(E_IPCCMD, IPCCmd)
+{
+    ATOMIC_PARAM(P_COMMAND, Command); // string
+    ATOMIC_PARAM(P_ID, ID); // unsigned
+}
+
+ATOMIC_EVENT(E_IPCCMDRESULT, IPCCmdResult)
+{
+    ATOMIC_PARAM(P_COMMAND, Command); // string
+    ATOMIC_PARAM(P_ID, ID); // unsigned
+}
+
 
 
 }
 }

+ 8 - 1
Source/Atomic/IPC/IPCServer.cpp

@@ -69,6 +69,9 @@ namespace Atomic
         SubscribeToEvent(E_UPDATE, ATOMIC_HANDLER(IPCServer, HandleUpdate));
         SubscribeToEvent(E_UPDATE, ATOMIC_HANDLER(IPCServer, HandleUpdate));
 
 
         SubscribeToEvent(serverBroker_, E_IPCCMDRESULT, ATOMIC_HANDLER(IPCServer, HandleIPCCmdResult));
         SubscribeToEvent(serverBroker_, E_IPCCMDRESULT, ATOMIC_HANDLER(IPCServer, HandleIPCCmdResult));
+
+        OnIPCWorkerStarted();
+
     }
     }
 
 
     void IPCServer::HandleIPCWorkerExit(StringHash eventType, VariantMap& eventData)
     void IPCServer::HandleIPCWorkerExit(StringHash eventType, VariantMap& eventData)
@@ -77,6 +80,8 @@ namespace Atomic
         {
         {
             serverBroker_ = 0;
             serverBroker_ = 0;
             brokerEnabled_ = false;
             brokerEnabled_ = false;
+
+            OnIPCWorkerExited();
         }
         }
     }
     }
 
 
@@ -93,6 +98,9 @@ namespace Atomic
 
 
         SendEvent("IPCServerLog", serverLogData);
         SendEvent("IPCServerLog", serverLogData);
 
 
+
+        OnIPCWorkerLog(eventData[P_LEVEL].GetInt(), eventData[P_MESSAGE].GetString());
+
     }
     }
 
 
     bool IPCServer::StartInternal(const String& exec, const Vector<String>& args)
     bool IPCServer::StartInternal(const String& exec, const Vector<String>& args)
@@ -177,7 +185,6 @@ namespace Atomic
 
 
     }
     }
 
 
-
     unsigned IPCServer::QueueCommand(IPCResultHandler* handler, const VariantMap& cmdMap)
     unsigned IPCServer::QueueCommand(IPCResultHandler* handler, const VariantMap& cmdMap)
     {
     {
         IPCCommand cmd;
         IPCCommand cmd;

+ 7 - 12
Source/Atomic/IPC/IPCServer.h

@@ -27,18 +27,6 @@
 
 
 namespace Atomic
 namespace Atomic
 {
 {
-    ATOMIC_EVENT(E_IPCCMD, IPCCmd)
-    {
-        ATOMIC_PARAM(P_COMMAND, Command); // string
-        ATOMIC_PARAM(P_ID, ID); // unsigned
-    }
-
-    ATOMIC_EVENT(E_IPCCMDRESULT, IPCCmdResult)
-    {
-        ATOMIC_PARAM(P_COMMAND, Command); // string
-        ATOMIC_PARAM(P_ID, ID); // unsigned
-    }
-
     class IPCBroker;
     class IPCBroker;
 
 
     /// IPCResultHandler
     /// IPCResultHandler
@@ -72,10 +60,17 @@ namespace Atomic
         virtual bool Start() = 0;
         virtual bool Start() = 0;
 
 
         unsigned QueueCommand(IPCResultHandler* handler, const VariantMap& cmdMap);
         unsigned QueueCommand(IPCResultHandler* handler, const VariantMap& cmdMap);
+
         bool GetBrokerEnabled() const;
         bool GetBrokerEnabled() const;
 
 
+        IPCBroker* GetServerBroker() const { return serverBroker_; }
+
     protected:
     protected:
 
 
+        virtual void OnIPCWorkerStarted() {}
+        virtual void OnIPCWorkerLog(int level, const String& message) {}
+        virtual void OnIPCWorkerExited() {}
+
         bool StartInternal(const String& exec, const Vector<String>& args);
         bool StartInternal(const String& exec, const Vector<String>& args);
 
 
     private:
     private:

+ 16 - 0
Source/Atomic/Math/Vector3.h

@@ -482,4 +482,20 @@ inline IntVector3 VectorMax(const IntVector3& lhs, const IntVector3& rhs) { retu
 /// Return a random value from [0, 1) from 3-vector seed.
 /// Return a random value from [0, 1) from 3-vector seed.
 inline float StableRandom(const Vector3& seed) { return StableRandom(Vector2(StableRandom(Vector2(seed.x_, seed.y_)), seed.z_)); }
 inline float StableRandom(const Vector3& seed) { return StableRandom(Vector2(StableRandom(Vector2(seed.x_, seed.y_)), seed.z_)); }
 
 
+// ATOMIC BEGIN
+
+inline float AreaOfTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2)
+  {
+
+    float a = (v0 - v1).Length();
+    float b = (v1 - v2).Length();
+    float c = (v2 - v0).Length();
+
+    float s = (a + b + c) * 0.5f;
+
+    return (float) Sqrt<float>(s * (s-a) * (s-b) * (s-c));
+  }
+
+// ATOMIC END
+
 }
 }

+ 4 - 0
Source/AtomicApp/IPCClientApp.h

@@ -44,6 +44,10 @@ namespace Atomic
 
 
         bool Initialize(const Vector<String>& arguments);
         bool Initialize(const Vector<String>& arguments);
 
 
+        bool GetBrokerActive() const { return brokerActive_; }
+
+        IPC* GetIPC() const { return ipc_; }
+
     private:
     private:
 
 
         void HandleLogMessage(StringHash eventType, VariantMap& eventData);
         void HandleLogMessage(StringHash eventType, VariantMap& eventData);

+ 15 - 1
Source/AtomicEditor/Application/AEEditorApp.cpp

@@ -38,6 +38,13 @@
 
 
 #include "../Components/EditorComponents.h"
 #include "../Components/EditorComponents.h"
 
 
+#ifdef ATOMIC_GLOW
+
+#include <AtomicGlow/GlowService/GlowService.h>
+using namespace AtomicGlow;
+
+#endif
+
 #include "AEEditorPrefs.h"
 #include "AEEditorPrefs.h"
 #include "AEEditorApp.h"
 #include "AEEditorApp.h"
 
 
@@ -145,8 +152,15 @@ namespace AtomicEditor
 
 
         context_->RegisterSubsystem(new EditorMode(context_));
         context_->RegisterSubsystem(new EditorMode(context_));
         context_->RegisterSubsystem(new NETBuildSystem(context_));
         context_->RegisterSubsystem(new NETBuildSystem(context_));
-        context_->RegisterSubsystem(new EditorNETService(context_));        
+        context_->RegisterSubsystem(new EditorNETService(context_));
 
 
+#ifdef ATOMIC_GLOW
+        SharedPtr<GlowService> glowService(new GlowService(context_));
+        if (glowService->Start())
+        {
+            context_->RegisterSubsystem(glowService);
+        }
+#endif
         AppBase::Start();
         AppBase::Start();
 
 
         vm_->SetModuleSearchPaths("AtomicEditor/JavaScript;AtomicEditor/EditorScripts;AtomicEditor/EditorScripts/AtomicEditor");
         vm_->SetModuleSearchPaths("AtomicEditor/JavaScript;AtomicEditor/EditorScripts;AtomicEditor/EditorScripts/AtomicEditor");

+ 2 - 0
Source/AtomicEditor/Components/EditorComponents.cpp

@@ -24,6 +24,7 @@
 
 
 #include "EditorComponent.h"
 #include "EditorComponent.h"
 #include "CubemapGenerator.h"
 #include "CubemapGenerator.h"
+#include "GlowComponent.h"
 
 
 namespace AtomicEditor
 namespace AtomicEditor
 {
 {
@@ -32,6 +33,7 @@ void RegisterEditorComponentLibrary(Atomic::Context* context)
 {
 {
     EditorComponent::RegisterObject(context);
     EditorComponent::RegisterObject(context);
     CubemapGenerator::RegisterObject(context);
     CubemapGenerator::RegisterObject(context);
+    GlowComponent::RegisterObject(context);
 }
 }
 
 
 }
 }

+ 143 - 0
Source/AtomicEditor/Components/GlowComponent.cpp

@@ -0,0 +1,143 @@
+//
+// Copyright (c) 2014-2017 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.
+//
+
+#include <Atomic/IO/Log.h>
+#include <Atomic/Core/Context.h>
+
+#include <AtomicGlow/GlowService/GlowService.h>
+#include "GlowComponent.h"
+
+using namespace AtomicGlow;
+
+namespace AtomicEditor
+{
+
+GlowComponent::GlowComponent(Context *context) : EditorComponent(context)
+{
+    GlowSettings glowSettings;
+    glowSettings.SetDefaults();
+    SetFromGlowSettings(glowSettings);
+}
+
+GlowComponent::~GlowComponent()
+{
+
+}
+
+void GlowComponent::SetFromGlowSettings(const GlowSettings& settings)
+{
+
+    lexelDensity_ = settings.lexelDensity_;
+
+    giEnabled_ = settings.giEnabled_;
+    giGranularity_ = settings.giGranularity_;
+    giMaxBounces_ = settings.giMaxBounces_;
+
+    aoEnabled_ = settings.aoEnabled_;
+    aoDepth_ = settings.aoDepth_;
+    nsamples_ = settings.nsamples_;
+    aoMin_ = settings.aoMin_;
+    aoMultiply_ = settings.aoMultiply_;
+
+}
+
+void GlowComponent::CopyToGlowSettings(GlowSettings& settings) const
+{
+    settings.lexelDensity_ = lexelDensity_ ;
+    settings.sceneLexelDensityScale_ = 1.0f;
+
+    settings.giEnabled_= giEnabled_;
+    settings.giGranularity_ = giGranularity_;
+    settings.giMaxBounces_ = giMaxBounces_;
+
+    settings.aoEnabled_ = aoEnabled_;
+    settings.aoDepth_ = aoDepth_;
+    settings.nsamples_ = nsamples_;
+    settings.aoMin_ = aoMin_;
+    settings.aoMultiply_ = aoMultiply_;
+}
+
+bool GlowComponent::Bake()
+{
+
+    GlowService* glowService = GetSubsystem<GlowService>();
+
+    if (!glowService)
+    {
+        ATOMIC_LOGERROR("GlowComponent::Bake() - Unable ot get glow service");
+        return false;
+    }
+
+    SubscribeToEvent(E_ATOMICGLOWSERVICEBAKERESULT, ATOMIC_HANDLER(GlowComponent, HandleAtomicGlowServiceBakeResult));
+    SubscribeToEvent(E_ATOMICGLOWSERVICELOGEVENT, ATOMIC_HANDLER(GlowComponent, HandleAtomicGlowServiceLogEvent));
+    SubscribeToEvent(E_ATOMICGLOWBAKECANCEL, ATOMIC_HANDLER(GlowComponent, HandleAtomicGlowBakeCancel));
+
+    GlowSettings settings;
+    CopyToGlowSettings(settings);
+    settings.Validate();
+    SetFromGlowSettings(settings);
+    return glowService->Bake(GetScene(), settings);
+}
+
+void GlowComponent::HandleAtomicGlowBakeCancel(StringHash eventType, VariantMap& eventData)
+{
+    GetSubsystem<GlowService>()->CancelBake();
+}
+
+void GlowComponent::HandleAtomicGlowServiceBakeResult(StringHash eventType, VariantMap& eventData)
+{
+    using namespace AtomicGlowServiceBakeResult;
+
+    // convert to a glow component event, which contains the same fields
+    SendEvent(E_ATOMICGLOWBAKERESULT, eventData);
+
+}
+
+void GlowComponent::HandleAtomicGlowServiceLogEvent(StringHash eventType, VariantMap& eventData)
+{
+    using namespace AtomicGlowServiceLogEvent;
+
+    // convert to a glow component event, which contains the same fields
+    SendEvent(E_ATOMICGLOWLOGEVENT, eventData);
+
+}
+
+void GlowComponent::RegisterObject(Context* context)
+{
+    context->RegisterFactory<GlowComponent>();
+
+    ATOMIC_ATTRIBUTE("Lexel Density", float, lexelDensity_, 0.1f, AM_FILE);
+
+    ATOMIC_ATTRIBUTE("GI Enabled", bool, giEnabled_, false, AM_FILE);
+    ATOMIC_ATTRIBUTE("GI Granularity", int, giGranularity_, 16, AM_FILE);
+    ATOMIC_ATTRIBUTE("GI Max Cycles", int, giMaxBounces_, 3, AM_FILE);
+
+    ATOMIC_ATTRIBUTE("AO Enabled", bool, aoEnabled_, false, AM_FILE);
+    ATOMIC_ATTRIBUTE("AO Depth", float, aoDepth_, 0.25f, AM_FILE);
+    ATOMIC_ATTRIBUTE("AO Samples", int, nsamples_, 64, AM_FILE);
+    ATOMIC_ATTRIBUTE("AO Min", float, aoMin_, 0.45f, AM_FILE);
+    ATOMIC_ATTRIBUTE("AO Multiply", float, aoMultiply_, 1.0f, AM_FILE);
+
+}
+
+
+}

+ 104 - 0
Source/AtomicEditor/Components/GlowComponent.h

@@ -0,0 +1,104 @@
+//
+// Copyright (c) 2014-2017 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.
+//
+
+#pragma once
+
+
+#include <AtomicGlow/Common/GlowSettings.h>
+#include <AtomicGlow/GlowService/GlowServiceEvents.h>
+
+#include "EditorComponent.h"
+
+using namespace Atomic;
+using namespace AtomicGlow;
+
+namespace AtomicEditor
+{
+
+// These glow events are here pretty much for editor access
+// due to Glow not currently having a scripting module
+ATOMIC_EVENT(E_ATOMICGLOWBAKECANCEL, AtomicGlowBakeCancel)
+{
+}
+
+ATOMIC_EVENT(E_ATOMICGLOWBAKERESULT, AtomicGlowBakeResult)
+{
+    ATOMIC_PARAM(P_SUCCESS, Success);    // bool
+    ATOMIC_PARAM(P_RESULT, Result);    // String
+}
+
+ATOMIC_EVENT(E_ATOMICGLOWLOGEVENT, AtomicGlowLogEvent)
+{
+    ATOMIC_PARAM(P_LEVEL, Level);    // bool
+    ATOMIC_PARAM(P_MESSAGE, Message);    // String
+}
+
+class GlowComponent : public EditorComponent
+{
+    ATOMIC_OBJECT(GlowComponent, EditorComponent)
+
+public:
+    /// Construct.
+    GlowComponent(Context* context);
+
+    /// Destruct.
+    virtual ~GlowComponent();
+
+    bool Bake();
+
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+
+protected:
+
+private:
+
+    void HandleAtomicGlowBakeCancel(StringHash eventType, VariantMap& eventData);
+
+    void HandleAtomicGlowServiceBakeResult(StringHash eventType, VariantMap& eventData);
+    void HandleAtomicGlowServiceLogEvent(StringHash eventType, VariantMap& eventData);
+
+    void SetFromGlowSettings(const GlowSettings& settings);
+
+    void CopyToGlowSettings(GlowSettings& settings) const;
+
+    // global scalar
+    float lexelDensity_;
+
+    // global illumination
+    bool giEnabled_;
+    int giGranularity_;
+    int giMaxBounces_;
+
+    // ambient occlusion
+    bool aoEnabled_;
+    float aoDepth_;
+    unsigned nsamples_;
+    float aoMin_;
+    float aoMultiply_;
+
+    WeakPtr<SceneEditor3D> sceneEditor_;
+
+};
+
+
+}

+ 320 - 0
Source/AtomicGlow/Atlas/MeshLightmapUVGen.cpp

@@ -0,0 +1,320 @@
+
+#include <ThirdParty/thekla/thekla_atlas.h>
+
+#include <Atomic/IO/Log.h>
+
+#include "ModelPacker.h"
+#include "MeshLightmapUVGen.h"
+
+
+namespace AtomicGlow
+{
+
+
+MeshLightmapUVGen::MeshLightmapUVGen(Context* context, Model* model, const String& modelName, const Settings& settings) : Object(context),
+    model_(model),
+    modelName_(modelName),
+    modelPacker_(new ModelPacker(context)),
+    settings_(settings),
+    tOutputMesh_(0),
+    tInputMesh_(0)
+{
+
+
+}
+
+MeshLightmapUVGen::~MeshLightmapUVGen()
+{
+
+}
+
+inline void MeshLightmapUVGen::EmitVertex(PODVector<MPVertex>& vertices, unsigned& index, const MPVertex& vertex)
+{
+    index = 0;
+
+    for (unsigned i = 0; i < vertices.Size(); i++)
+    {
+        if (vertices[i] == vertex)
+        {
+            index = i;
+            return;
+        }
+    }
+
+    index = vertices.Size();
+    vertices.Push(vertex);
+}
+
+void MeshLightmapUVGen::WriteLightmapUVCoords()
+{
+    String modelName = modelName_;
+
+    if (!modelName.Length())
+        modelName = "AnonymousModel";
+
+    
+    //Thekla::atlas_write_debug_textures(tOutputMesh_, tInputMesh_, ToString("/Users/jenge/Desktop/%s_lmWorldSpaceTexture.png", modelName.CString()).CString() ,
+    //                                                              ToString("/Users/jenge/Desktop/%s_lmNormalTexture.png", modelName.CString()).CString() );
+
+    Vector<PODVector<MPVertex>> geoVerts;
+    Vector<PODVector<unsigned>> geoIndices;
+
+    geoVerts.Resize(curLOD_->mpGeometry_.Size());
+    geoIndices.Resize(curLOD_->mpGeometry_.Size());
+
+    float uscale = 1.f / tOutputMesh_->atlas_width;
+    float vscale = 1.f / tOutputMesh_->atlas_height;
+
+    for (unsigned i = 0; i < tOutputMesh_->index_count/3; i++)
+    {
+        unsigned v0 = (unsigned) tOutputMesh_->index_array[i * 3];
+        unsigned v1 = (unsigned) tOutputMesh_->index_array[i * 3 + 1];
+        unsigned v2 = (unsigned) tOutputMesh_->index_array[i * 3 + 2];
+
+        Thekla::Atlas_Output_Vertex& tv0 = tOutputMesh_->vertex_array[v0];
+        Thekla::Atlas_Output_Vertex& tv1 = tOutputMesh_->vertex_array[v1];
+        Thekla::Atlas_Output_Vertex& tv2 = tOutputMesh_->vertex_array[v2];
+
+        LMVertex& lv0 = lmVertices_[tv0.xref];
+        LMVertex& lv1 = lmVertices_[tv1.xref];
+        LMVertex& lv2 = lmVertices_[tv2.xref];
+
+        unsigned geometryIdx = lv0.geometryIdx_;
+
+        // check for mixed geometry in triangle
+        if (geometryIdx != lv1.geometryIdx_ || geometryIdx != lv2.geometryIdx_)
+        {
+            assert(0);
+        }
+
+        MPGeometry* mpGeo = curLOD_->mpGeometry_[geometryIdx];
+
+        PODVector<MPVertex>& verts = geoVerts[geometryIdx];
+        PODVector<unsigned>& indices = geoIndices[geometryIdx];
+
+        unsigned ovindices[3];
+        Vector2 uvs[3];
+
+        uvs[0] = Vector2(tv0.uv[0], tv0.uv[1]);
+        uvs[1] = Vector2(tv1.uv[0], tv1.uv[1]);
+        uvs[2] = Vector2(tv2.uv[0], tv2.uv[1]);
+
+        ovindices[0] = lv0.originalVertex_;
+        ovindices[1] = lv1.originalVertex_;
+        ovindices[2] = lv2.originalVertex_;
+
+        Vector2 center(uvs[0]);
+        center += uvs[1];
+        center += uvs[2];
+        center /= 3.0f;
+
+        unsigned index;
+        for (unsigned j = 0; j < 3; j++)
+        {
+            Vector2 uv = uvs[j];
+
+            /*
+            uv -= center;
+            uv *= 0.98f;
+            uv += center;
+
+            uv.x_ = Clamp<float>(uv.x_, 2, tOutputMesh_->atlas_width - 2);
+            uv.y_ = Clamp<float>(uv.y_, 2, tOutputMesh_->atlas_height - 2);
+            */
+
+            uv.x_ *= uscale;
+            uv.y_ *= vscale;
+
+            MPVertex mpv = mpGeo->vertices_[ovindices[j]];
+            mpv.uv1_ = uv;
+            EmitVertex(verts, index, mpv);
+            indices.Push(index);
+        }
+    }
+
+    for (unsigned i = 0; i < curLOD_->mpGeometry_.Size(); i++)
+    {
+        MPGeometry* mpGeo = curLOD_->mpGeometry_[i];
+
+        mpGeo->vertices_ = geoVerts[i];
+        mpGeo->indices_ = new unsigned[geoIndices[i].Size()];
+        memcpy(&mpGeo->indices_[0], &geoIndices[i][0], sizeof(unsigned) * geoIndices[i].Size());
+        mpGeo->numIndices_ = geoIndices[i].Size();
+
+        // Check whether we need to add UV1 semantic
+
+        PODVector<VertexElement> nElements;
+
+        unsigned texCoordCount = 0;
+        for (unsigned j = 0; j < mpGeo->elements_.Size(); j++)
+        {
+            VertexElement element = mpGeo->elements_[j];
+
+            if (element.type_ == TYPE_VECTOR2 && element.semantic_ == SEM_TEXCOORD)
+                texCoordCount++;
+        }
+
+        if (texCoordCount == 0)
+        {
+            // We don't have a valid UV set in UV0
+            // FIX ME: This doesn't currently work
+            mpGeo->elements_.Push(VertexElement(TYPE_VECTOR2, SEM_TEXCOORD, 0));
+            mpGeo->elements_.Push(VertexElement(TYPE_VECTOR2, SEM_TEXCOORD, 1));
+        }
+        else if (texCoordCount == 1)
+        {
+            bool added = false;
+            for (unsigned j = 0; j < mpGeo->elements_.Size(); j++)
+            {
+                VertexElement element = mpGeo->elements_[j];
+
+                nElements.Push(element);
+
+                if ( (element.type_ == TYPE_VECTOR2 && element.semantic_ == SEM_TEXCOORD) || (!added && j == (mpGeo->elements_.Size() - 1) ) )
+                {
+                    added = true;
+                    VertexElement element(TYPE_VECTOR2, SEM_TEXCOORD, 1);
+                    nElements.Push(element);
+                }
+
+            }
+
+            mpGeo->elements_ = nElements;
+        }
+
+    }
+
+}
+
+bool MeshLightmapUVGen::Generate()
+{
+    if (model_.Null())
+        return false;
+
+    if (!modelPacker_->Unpack(model_))
+    {
+        return false;
+    }
+
+    for (unsigned i = 0; i < modelPacker_->lodLevels_.Size(); i++)
+    {
+
+        curLOD_ = modelPacker_->lodLevels_[i];
+
+        // combine all LOD vertices/indices
+
+        unsigned totalVertices = 0;
+        unsigned totalIndices = 0;
+        for (unsigned j = 0; j < curLOD_->mpGeometry_.Size(); j++)
+        {
+            MPGeometry* geo = curLOD_->mpGeometry_[j];
+            totalVertices += geo->vertices_.Size();
+            totalIndices += geo->numIndices_;
+        }
+
+        // Setup thekla input mesh
+        tInputMesh_ = new Thekla::Atlas_Input_Mesh();
+
+        // Allocate vertex arrays
+        lmVertices_ = new LMVertex[totalVertices];
+
+        tInputMesh_->vertex_count = totalVertices;
+        tInputMesh_->vertex_array = new Thekla::Atlas_Input_Vertex[tInputMesh_->vertex_count];
+
+        tInputMesh_->face_count = totalIndices / 3;
+        tInputMesh_->face_array = new Thekla::Atlas_Input_Face[tInputMesh_->face_count];
+
+        unsigned vCount = 0;
+        unsigned fCount = 0;
+
+        for (unsigned j = 0; j < curLOD_->mpGeometry_.Size(); j++)
+        {
+            MPGeometry* geo = curLOD_->mpGeometry_[j];
+
+            unsigned vertexStart = vCount;
+
+            for (unsigned k = 0; k < geo->vertices_.Size(); k++, vCount++)
+            {
+                const MPVertex& mpv = geo->vertices_[k];
+
+                LMVertex& lmv = lmVertices_[vCount];
+                Thekla::Atlas_Input_Vertex& tv = tInputMesh_->vertex_array[vCount];
+
+                lmv.geometry_ = geo;
+                lmv.geometryIdx_ = j;
+                lmv.originalVertex_ = k;
+
+                tv.position[0] = mpv.position_.x_;
+                tv.position[1] = mpv.position_.y_;
+                tv.position[2] = mpv.position_.z_;
+
+                tv.normal[0] = mpv.normal_.x_;
+                tv.normal[1] = mpv.normal_.y_;
+                tv.normal[2] = mpv.normal_.z_;
+
+                tv.uv[0] = mpv.uv0_.x_;
+                tv.uv[1] = mpv.uv0_.y_;
+
+                // this appears unused in thekla atlas?
+                tv.first_colocal = vCount;
+
+            }
+
+            for (unsigned k = 0; k < geo->numIndices_/3; k++, fCount++)
+            {
+                Thekla::Atlas_Input_Face& tface = tInputMesh_->face_array[fCount];
+
+                tface.vertex_index[0] = (int) (geo->indices_[k * 3] + vertexStart);
+                tface.vertex_index[1] = (int) (geo->indices_[k * 3 + 1] + vertexStart);
+                tface.vertex_index[2] = (int) (geo->indices_[k * 3 + 2] + vertexStart);
+
+                if (tface.vertex_index[0] > totalVertices || tface.vertex_index[1] > totalVertices || tface.vertex_index[2] > totalVertices)
+                {
+                    ATOMIC_LOGERROR("Vertex overflow");
+                    return false;
+                }
+
+                tface.material_index = j;
+
+            }
+
+        }
+
+        Thekla::Atlas_Options atlasOptions;
+        atlas_set_default_options(&atlasOptions);
+
+        // disable brute force packing quality, as it has a number of notes about performance
+        // and it is turned off in Thekla example in repo as well.  I am also seeing some meshes
+        // having problems packing with it and hanging on import
+        atlasOptions.packer_options.witness.packing_quality = 1;
+
+        atlasOptions.packer_options.witness.texel_area = 8;
+        atlasOptions.packer_options.witness.conservative = true;
+
+        Thekla::Atlas_Error error = Thekla::Atlas_Error_Success;
+
+        tOutputMesh_ = atlas_generate(tInputMesh_, &atlasOptions, &error);
+
+        if (tOutputMesh_)
+        {
+            WriteLightmapUVCoords();
+        }
+
+        delete [] tInputMesh_->vertex_array;
+        delete [] tInputMesh_->face_array;
+        delete tInputMesh_;
+        tInputMesh_ = 0;
+
+        atlas_free(tOutputMesh_);
+        tOutputMesh_ = 0;
+
+    }
+
+    // update model
+    modelPacker_->Pack();
+
+    return true;
+
+}
+
+}

+ 71 - 0
Source/AtomicGlow/Atlas/MeshLightmapUVGen.h

@@ -0,0 +1,71 @@
+
+#pragma once
+
+#include "ModelPacker.h"
+
+using namespace Atomic;
+
+namespace Thekla
+{
+
+struct Atlas_Output_Mesh;
+struct Atlas_Input_Mesh;
+
+}
+
+namespace AtomicGlow
+{
+
+class ModelPacker;
+
+class MeshLightmapUVGen : public Object
+{
+    ATOMIC_OBJECT(MeshLightmapUVGen, Object)
+
+public:
+
+    struct Settings
+    {
+        bool genChartID_;
+
+        Settings()
+        {
+            genChartID_ = false;
+        }
+
+    };
+
+    MeshLightmapUVGen(Context* context, Model* model, const String& modelName, const Settings& settings);
+    virtual ~MeshLightmapUVGen();
+
+    bool Generate();
+
+private:
+
+    inline void EmitVertex(PODVector<MPVertex>& vertices, unsigned& index, const MPVertex& vertex);
+
+    void WriteLightmapUVCoords();
+
+    struct LMVertex
+    {
+        MPGeometry* geometry_;
+        unsigned geometryIdx_;
+        unsigned originalVertex_;
+    };
+
+    SharedPtr<Model> model_;
+    String modelName_;
+    SharedPtr<ModelPacker> modelPacker_;
+
+    SharedPtr<MPLODLevel> curLOD_;
+
+    SharedArrayPtr<LMVertex> lmVertices_;
+
+    Settings settings_;
+
+    Thekla::Atlas_Output_Mesh* tOutputMesh_;
+    Thekla::Atlas_Input_Mesh* tInputMesh_;
+
+};
+
+}

+ 532 - 0
Source/AtomicGlow/Atlas/ModelPacker.cpp

@@ -0,0 +1,532 @@
+
+#include <Atomic/Graphics/IndexBuffer.h>
+#include <Atomic/Graphics/VertexBuffer.h>
+
+#include "ModelPacker.h"
+
+namespace AtomicGlow
+{
+
+ModelPacker::ModelPacker(Context* context) : Object(context)
+{
+
+}
+
+ModelPacker::~ModelPacker()
+{
+
+}
+
+/// Get the total vertex and index counts of all LOD geometry
+void MPLODLevel::GetTotalCounts(unsigned& totalVertices, unsigned& totalIndices) const
+{
+    totalVertices = 0;
+    totalIndices = 0;
+
+    for (unsigned i = 0; i < mpGeometry_.Size(); i++)
+    {
+        MPGeometry* mpGeo = mpGeometry_[i];
+
+        totalVertices += mpGeo->vertices_.Size();
+        totalIndices += mpGeo->numIndices_;
+    }
+
+}
+
+/// Returns true if all LOD geometry contains element
+bool MPLODLevel::HasElement(VertexElementType type, VertexElementSemantic semantic, unsigned char index) const
+{
+    if (!mpGeometry_.Size())
+        return false;
+
+    for (unsigned i = 0; i < mpGeometry_.Size(); i++)
+    {
+        MPGeometry* mpGeo = mpGeometry_[i];
+
+        if (!VertexBuffer::HasElement(mpGeo->elements_, type, semantic, index))
+            return false;
+
+    }
+
+    return true;
+
+}
+
+
+bool ModelPacker::Pack()
+{
+
+    if (model_.Null())
+    {
+        SetError("No model to pack");
+        return false;
+    }
+
+    for (unsigned i = 0; i < lodLevels_.Size(); i++)
+    {
+        MPLODLevel* lodLevel = lodLevels_[i];
+
+        if (!lodLevel->mpGeometry_.Size())
+            continue;
+
+        unsigned totalVertices = 0;
+        unsigned totalIndices = 0;
+
+        for (unsigned j = 0; j < lodLevel->mpGeometry_.Size(); j++)
+        {
+            totalVertices += lodLevel->mpGeometry_[j]->vertices_.Size();
+            totalIndices += lodLevel->mpGeometry_[j]->numIndices_;
+        }
+
+        bool combineBuffers = true;
+
+        // Check if buffers can be combined (same vertex element mask, under 65535 vertices)
+        const PODVector<VertexElement>& elements = lodLevel->mpGeometry_[0]->elements_;
+
+        for (unsigned j = 1; j < lodLevel->mpGeometry_.Size(); j++)
+        {
+            if (elements != lodLevel->mpGeometry_[j]->elements_)
+            {
+                combineBuffers = false;
+                break;
+            }
+
+        }
+
+        // Check if keeping separate buffers allows to avoid 32-bit indices
+        if (combineBuffers && totalVertices > 65535)
+        {
+            bool allUnder65k = true;
+
+            for (unsigned j = 0; j < lodLevel->mpGeometry_.Size(); j++)
+            {
+                if (lodLevel->mpGeometry_[j]->vertices_.Size() > 65535)
+                {
+                    allUnder65k = false;
+                    break;
+                }
+            }
+
+            if (allUnder65k == true)
+                combineBuffers = false;
+        }
+
+        SharedPtr<IndexBuffer> ib;
+        SharedPtr<VertexBuffer> vb;
+        Vector<SharedPtr<VertexBuffer> > vbVector;
+        Vector<SharedPtr<IndexBuffer> > ibVector;
+        unsigned startVertexOffset = 0;
+        unsigned startIndexOffset = 0;
+
+        for (unsigned j = 0; j < lodLevel->mpGeometry_.Size(); j++)
+        {
+
+            MPGeometry* mpGeo = lodLevel->mpGeometry_[j];
+
+            bool largeIndices;
+
+            if (combineBuffers)
+                largeIndices = totalIndices > 65535;
+            else
+                largeIndices = mpGeo->vertices_.Size() > 65535;
+
+            // Create new buffers if necessary
+            if (!combineBuffers || vbVector.Empty())
+            {
+                vb = new VertexBuffer(context_);
+                ib = new IndexBuffer(context_);
+
+                vb->SetShadowed(true);
+                ib->SetShadowed(true);
+
+                if (combineBuffers)
+                {
+                    ib->SetSize(totalIndices, largeIndices);
+                    vb->SetSize(totalVertices, mpGeo->elements_);
+                }
+                else
+                {
+                    ib->SetSize(mpGeo->numIndices_, largeIndices);
+                    vb->SetSize(mpGeo->vertices_.Size(), mpGeo->elements_);
+                }
+
+                vbVector.Push(vb);
+                ibVector.Push(ib);
+                startVertexOffset = 0;
+                startIndexOffset = 0;
+            }
+
+            unsigned char* vertexData = vb->GetShadowData();
+            unsigned char* indexData = ib->GetShadowData();
+
+            assert(vertexData);
+            assert(indexData);
+
+            // Build the index data
+            if (!largeIndices)
+            {
+                unsigned short* dest = (unsigned short*)indexData + startIndexOffset;
+
+                for (unsigned k = 0; k < mpGeo->numIndices_; k++)
+                {
+                    *dest++ = (unsigned short) (mpGeo->indices_[k] + startVertexOffset);
+                }
+            }
+            else
+            {
+                unsigned* dest = (unsigned*)indexData + startIndexOffset;
+
+                for (unsigned k = 0; k < mpGeo->numIndices_; k++)
+                {
+                    *dest++ = mpGeo->indices_[k] + startVertexOffset;
+                }
+
+            }
+
+            // Build vertex data
+            float* vertexDest = (float*)((unsigned char*)vertexData + startVertexOffset * vb->GetVertexSize());
+
+            for (unsigned k = 0; k < mpGeo->vertices_.Size(); k++)
+            {
+                const MPVertex& vertex = mpGeo->vertices_[k];
+
+                unsigned texCoordCount = 0;
+
+                const PODVector<VertexElement>& elements = vb->GetElements();
+
+                for (unsigned x = 0; x < elements.Size(); x++)
+                {
+                    VertexElement element = elements[x];
+
+                    if (element.type_ == TYPE_VECTOR3 && element.semantic_ == SEM_POSITION)
+                    {
+                        *vertexDest++ = vertex.position_.x_;
+                        *vertexDest++ = vertex.position_.y_;
+                        *vertexDest++ = vertex.position_.z_;
+                    }
+                    if (element.type_ == TYPE_VECTOR3 && element.semantic_ == SEM_NORMAL)
+                    {
+                        *vertexDest++ = vertex.normal_.x_;
+                        *vertexDest++ = vertex.normal_.y_;
+                        *vertexDest++ = vertex.normal_.z_;
+                    }
+                    if (element.type_ == TYPE_UBYTE4 && element.semantic_ == SEM_COLOR)
+                    {
+                        *((unsigned*)vertexDest) = vertex.color_;
+                        vertexDest++;
+                    }
+                    if (element.type_ == TYPE_VECTOR2 && element.semantic_ == SEM_TEXCOORD)
+                    {
+                        if (texCoordCount == 0)
+                        {
+                            *vertexDest++ = vertex.uv0_.x_;
+                            *vertexDest++ = vertex.uv0_.y_;
+                        }
+                        else
+                        {
+
+                            *vertexDest++ = vertex.uv1_.x_;
+                            *vertexDest++ = vertex.uv1_.y_;
+                        }
+
+                        texCoordCount++;
+                    }
+                    if (element.type_ == TYPE_VECTOR4 && element.semantic_ == SEM_TANGENT)
+                    {
+                        *vertexDest++ = vertex.tangent_.x_;
+                        *vertexDest++ = vertex.tangent_.y_;
+                        *vertexDest++ = vertex.tangent_.z_;
+                        *vertexDest++ = vertex.tangent_.w_;
+                    }
+
+                }
+
+            }
+
+            // Update geometry, note that this assumes geometry center, etc are consistent with original
+            Geometry* geom = mpGeo->geometry_;
+
+            geom->SetIndexBuffer(ib);
+            geom->SetVertexBuffer(0, vb);
+            geom->SetDrawRange(TRIANGLE_LIST, startIndexOffset, mpGeo->numIndices_, true);
+
+            startVertexOffset += mpGeo->vertices_.Size();
+            startIndexOffset += mpGeo->numIndices_;
+        }
+
+        // update model
+        PODVector<unsigned> emptyMorphRange;
+        model_->SetVertexBuffers(vbVector, emptyMorphRange, emptyMorphRange);
+        model_->SetIndexBuffers(ibVector);
+
+    }
+
+    return true;
+}
+
+bool ModelPacker::Unpack(Model* model)
+{
+
+    model_ = model;
+
+    unsigned maxLOD = 0;
+
+    for (unsigned i = 0; i < model_->GetNumGeometries(); i++)
+    {
+        unsigned numLOD = model_->GetNumGeometryLodLevels(i);
+
+        if (numLOD > maxLOD)
+            maxLOD = numLOD;
+    }
+
+    if (!maxLOD)
+    {
+        SetError("No LOD in model");
+        return false;
+    }
+
+    for (unsigned i = 0; i < maxLOD; i++)
+    {
+        if (!UnpackLODLevel(i))
+            return false;
+    }
+
+    return true;
+}
+
+bool ModelPacker::UnpackLODLevel(unsigned level)
+{
+    PODVector<Geometry*> lodGeo;
+
+    for (unsigned i = 0; i < model_->GetNumGeometries(); i++)
+    {
+        Geometry * geo = model_->GetGeometry(i, level);
+
+        if (geo)
+            lodGeo.Push(geo);
+    }
+
+    if (!level && !lodGeo.Size())
+    {
+        SetError("No geometry in LOD 0 for model");
+        return false;
+    }
+
+    if (!lodGeo.Size())
+        return true;
+
+    SharedPtr<MPLODLevel> lodLevel (new MPLODLevel());
+
+    for (unsigned i = 0; i < lodGeo.Size(); i++)
+    {
+        if (!UnpackGeometry(lodLevel, lodGeo[i]))
+            return false;
+    }
+
+    lodLevel->level_ = level;
+
+    lodLevels_.Push(lodLevel);
+
+    return true;
+
+}
+
+bool ModelPacker::UnpackGeometry(MPLODLevel *level, Geometry* geometry)
+{
+    SharedPtr<MPGeometry> mpGeo(new MPGeometry());
+
+    mpGeo->geometry_ = geometry;
+
+    // We only support vertex buffer operations on vertex buffer 0
+    // TODO: should it be an error if > 1 vertex buffer in geo since we might be destructive to index buffers?
+
+    const unsigned char* indexData = 0;
+    unsigned indexSize = 0;
+
+    unsigned vertexSize = 0;
+    const unsigned char* vertexData = 0;
+
+    const PODVector<VertexElement>* elements = 0;
+
+    geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elements);
+
+    if (!indexData || !indexSize || !vertexData || !vertexSize || !elements)
+    {
+        SetError("ModelPacker::UnpackGeometry - Failed to get raw data for geometry");
+        return false;
+    }
+
+    // VERTEX DATA
+
+    mpGeo->elements_ = *elements;
+
+    const unsigned char* positionData = 0;
+    const unsigned char* normalData = 0;
+    const unsigned char* tangentData = 0;
+    const unsigned char* colorData = 0;
+    const unsigned char* uv0Data = 0;
+    const unsigned char* uv1Data = 0;
+
+    unsigned vertexStart = geometry->GetVertexStart();
+    unsigned vertexCount = geometry->GetVertexCount();
+
+    vertexData += vertexStart * vertexSize;
+
+    for (unsigned i = 0; i < elements->Size(); i++)
+    {
+        VertexElement element = elements->At(i);
+
+        if (element.type_ == TYPE_VECTOR3 && element.semantic_ == SEM_POSITION)
+        {
+            positionData = vertexData + element.offset_;
+        }
+        else if (element.type_ == TYPE_VECTOR3 && element.semantic_ == SEM_NORMAL)
+        {
+            normalData = vertexData + element.offset_;
+        }
+        else if (element.type_ == TYPE_UBYTE4 && element.semantic_ == SEM_COLOR)
+        {
+            colorData = vertexData + element.offset_;
+        }
+        else if (element.type_ == TYPE_VECTOR4 && element.semantic_ == SEM_TANGENT)
+        {
+            tangentData = vertexData + element.offset_;
+        }
+        else if (element.type_ == TYPE_VECTOR2 && element.semantic_ == SEM_TEXCOORD)
+        {
+            if (!uv0Data)
+            {
+                uv0Data = vertexData + element.offset_;
+            }
+            else
+            {
+                uv1Data = vertexData + element.offset_;
+            }
+        }
+    }
+
+    if (!positionData)
+    {
+        SetError("Geometry has no position data");
+        return false;
+    }
+
+    mpGeo->vertices_.Resize(vertexCount);
+
+    MPVertex* v = &mpGeo->vertices_[0];
+
+    v->Clear();
+
+    for (unsigned i = 0; i < vertexCount; i++, v++)
+    {
+        float* fp = (float *) positionData;
+
+        v->position_.x_ = fp[0];
+        v->position_.y_ = fp[1];
+        v->position_.z_ = fp[2];
+
+        positionData += vertexSize;
+
+        if (normalData)
+        {
+            fp = (float *) normalData;
+
+            v->normal_.x_ = fp[0];
+            v->normal_.y_ = fp[1];
+            v->normal_.z_ = fp[2];
+
+            normalData += vertexSize;
+        }
+
+        if (tangentData)
+        {
+            fp = (float *) tangentData;
+
+            v->tangent_.x_ = fp[0];
+            v->tangent_.y_ = fp[1];
+            v->tangent_.z_ = fp[2];
+            v->tangent_.w_ = fp[3];
+
+            tangentData += vertexSize;
+        }
+
+        if (uv0Data)
+        {
+            fp = (float *) uv0Data;
+
+            v->uv0_.x_ = fp[0];
+            v->uv0_.y_ = fp[1];
+
+            uv0Data += vertexSize;
+        }
+
+        if (uv1Data)
+        {
+            fp = (float *) uv1Data;
+
+            v->uv1_.x_ = fp[0];
+            v->uv1_.y_ = fp[1];
+
+            uv1Data += vertexSize;
+        }
+
+        if (colorData)
+        {
+            v->color_ = *((unsigned *)colorData);
+            colorData += vertexSize;
+        }
+
+    }
+
+    // INDICES
+
+    unsigned indexStart = geometry->GetIndexStart();
+    unsigned indexCount = geometry->GetIndexCount();
+
+    // source indices converted to unsigned value
+    PODVector<unsigned> geoIndices;
+
+    if (indexSize == sizeof(unsigned short))
+    {
+        // 16-bit indices
+        const unsigned short* indices = ((const unsigned short*)indexData) + indexStart;
+        const unsigned short* indicesEnd = indices + indexCount;
+
+        while (indices < indicesEnd)
+        {
+            unsigned idx = (unsigned) *indices++;
+
+            if (idx >= vertexStart && idx < (vertexStart + vertexCount))
+            {
+                geoIndices.Push(idx - vertexStart);
+            }
+        }
+    }
+    else
+    {
+        // 32-bit indices
+        const unsigned* indices = ((const unsigned*)indexData) + indexStart;
+        const unsigned* indicesEnd = indices + indexCount;
+
+        while (indices < indicesEnd)
+        {
+            unsigned idx = *indices++;
+
+            if (idx >= vertexStart && idx < (vertexStart + vertexCount))
+            {
+                geoIndices.Push(idx - vertexStart);
+            }
+        }
+
+    }
+
+    mpGeo->indices_ = new unsigned[geoIndices.Size()];
+    mpGeo->numIndices_ = geoIndices.Size();
+    memcpy(&mpGeo->indices_[0], &geoIndices[0], sizeof(unsigned) * geoIndices.Size());
+
+    level->mpGeometry_.Push(mpGeo);
+
+    return true;
+}
+
+}

+ 109 - 0
Source/AtomicGlow/Atlas/ModelPacker.h

@@ -0,0 +1,109 @@
+#pragma once
+
+#include <Atomic/Graphics/IndexBuffer.h>
+#include <Atomic/Graphics/VertexBuffer.h>
+#include <Atomic/Graphics/Geometry.h>
+#include <Atomic/Graphics/Model.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+class MPGeometry;
+
+struct MPVertex
+{
+    Vector3 position_;
+    Vector3 normal_;
+    Vector4 tangent_;
+    unsigned color_;
+    Vector2 uv0_;
+    Vector2 uv1_;
+
+    void Clear()
+    {
+        position_ =  Vector3::ZERO;
+        normal_ = Vector3::ZERO;
+        tangent_ = Vector4::ZERO;
+        uv0_ = Vector2::ZERO;
+        uv1_ = Vector2::ZERO;
+        color_ = 0;
+    }
+
+    bool operator==(const MPVertex& rhs)
+    {
+        return ( position_ == rhs.position_ &&
+                 normal_ == rhs.normal_ &&
+                 tangent_ == rhs.tangent_ &&
+                 color_ == rhs.color_ &&
+                 uv0_ == rhs.uv0_ &&
+                 uv1_ == rhs.uv1_ );
+
+    }
+};
+
+class MPGeometry : public RefCounted
+{
+    ATOMIC_REFCOUNTED(MPGeometry)
+
+public:
+
+    SharedPtr<Geometry> geometry_;
+    PODVector<MPVertex> vertices_;
+    PODVector<VertexElement> elements_;
+    SharedArrayPtr<unsigned> indices_;
+    unsigned numIndices_;
+
+};
+
+class MPLODLevel : public RefCounted
+{
+    ATOMIC_REFCOUNTED(MPLODLevel)
+
+public:
+
+    /// Get the total vertex and index counts of all LOD geometry
+    void GetTotalCounts(unsigned& totalVertices, unsigned& totalIndices) const;
+
+    /// Returns true if all LOD geometry contains element
+    bool HasElement(VertexElementType type, VertexElementSemantic semantic, unsigned char index = 0) const;
+
+    Vector<SharedPtr<MPGeometry>> mpGeometry_;
+
+    unsigned level_;
+};
+
+/// Model packer/unpacker from/to packed representation (destructive!)
+class ModelPacker : public Object
+{
+    ATOMIC_OBJECT(ModelPacker, Object)
+
+public:
+
+    ModelPacker(Context* context);
+    virtual ~ModelPacker();
+
+    bool Unpack(Model* model);
+
+    bool Pack();
+
+    const String& GetErrorText() const { return errorText_; }
+
+    Vector<SharedPtr<MPLODLevel>> lodLevels_;
+
+private:
+
+    void SetError(const String& errorText) { errorText_ = errorText; }
+
+    String errorText_;
+
+    bool UnpackLODLevel(unsigned level);
+
+    bool UnpackGeometry(MPLODLevel* level, Geometry* geometry);
+
+    SharedPtr<Model> model_;
+
+};
+
+}

+ 32 - 0
Source/AtomicGlow/CMakeLists.txt

@@ -0,0 +1,32 @@
+include_directories ( ${CMAKE_SOURCE_DIR}/Source/ThirdParty )
+include_directories ( ${CMAKE_SOURCE_DIR}/Source/ThirdParty/embree/include )
+
+add_definitions(-DEMBREE_STATIC_LIB=1)
+
+file (GLOB GLOW_COMMON_SOURCE Common/*.cpp Common/*.h)
+file (GLOB GLOW_KERNEL_SOURCE Kernel/*.cpp Kernel/*.h)
+file (GLOB GLOW_ATLAS_SOURCE Atlas/*.cpp Atlas/*.h)
+file (GLOB GLOW_SERVICE_SOURCE GlowService/*.cpp GlowService/*.h)
+
+add_library(AtomicGlowLib ${GLOW_COMMON_SOURCE} ${GLOW_KERNEL_SOURCE} ${GLOW_ATLAS_SOURCE} ${GLOW_SERVICE_SOURCE} )
+
+target_link_libraries(AtomicGlowLib ToolCore Poco embree)
+
+target_compile_definitions(AtomicGlowLib PUBLIC -DATOMIC_GLOW=1)
+
+add_dependencies(AtomicApp AtomicToolCheckScripts)
+
+if (WIN32)
+    add_definitions(-DATOMIC_WIN32_CONSOLE)
+endif(WIN32)
+
+file (GLOB GLOW_APPLICATION_SOURCE GlowApplication/*.cpp GlowApplication/*.h)
+
+add_executable(AtomicGlow ${GLOW_APPLICATION_SOURCE})
+
+target_link_libraries(AtomicGlow AtomicApp AtomicJS AtomicNETScript AtomicGlowLib)
+
+if (WIN32)
+    target_link_libraries(AtomicGlow Iphlpapi Wldap32)
+endif()
+

+ 40 - 0
Source/AtomicGlow/Common/GlowEvents.h

@@ -0,0 +1,40 @@
+//
+// 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.
+//
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+// Glow process -> Glow Service event
+ATOMIC_EVENT(E_ATOMICGLOWRESULT, AtomicGlowResult)
+{
+    ATOMIC_PARAM(P_SUCCESS, Success);    // bool
+    ATOMIC_PARAM(P_RESULT, Result);    // String
+    ATOMIC_PARAM(P_BAKEDATA, BakeData);  // VectorBuffer
+}
+
+}

+ 30 - 0
Source/AtomicGlow/Common/GlowSettings.cpp

@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2014-2017, 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 "GlowSettings.h"
+
+namespace AtomicGlow
+{
+
+GlowSettings GlobalGlowSettings;
+
+}

+ 192 - 0
Source/AtomicGlow/Common/GlowSettings.h

@@ -0,0 +1,192 @@
+//
+// Copyright (c) 2014-2017, 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/Math/MathDefs.h>
+#include <Atomic/Container/Str.h>
+#include <Atomic/IO/VectorBuffer.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+
+{
+    enum GlowPreset
+    {
+        GLOW_PRESET_FAST_LOW_QUALITY,
+        GLOW_PRESET_MEDIUM_QUALITY,
+        GLOW_PRESET_HIGH_QUALITY,
+        GLOW_PRESET_SLOW_EXTREME_QUALITY
+    };
+
+    enum GlowOutputFormat
+    {
+        GLOW_OUTPUT_PNG,
+        GLOW_OUTPUT_DDS
+    };
+
+    struct GlowSettings
+    {
+        int lightmapSize_;
+        GlowOutputFormat outputFormat_;
+
+        // global scalar
+        float lexelDensity_;
+
+        float sceneLexelDensityScale_;
+
+        // global illumination
+        bool giEnabled_;
+        int giGranularity_;
+        int giMaxBounces_;
+
+        // ambient occlusion
+        bool aoEnabled_;
+        float aoDepth_;
+        unsigned nsamples_;
+        float aoMin_;
+        float aoMultiply_;
+
+
+        String projectPath_;
+
+        GlowSettings()
+        {
+            SetDefaults();
+            Validate();
+        }
+
+        void Pack(VectorBuffer& buffer) const
+        {
+            buffer.WriteInt(lightmapSize_);
+            buffer.WriteUInt((unsigned) outputFormat_);
+
+            buffer.WriteFloat(lexelDensity_);
+            buffer.WriteFloat(sceneLexelDensityScale_);
+
+            buffer.WriteBool(giEnabled_);
+            buffer.WriteInt(giGranularity_);
+            buffer.WriteInt(giMaxBounces_);
+
+            buffer.WriteBool(aoEnabled_);
+            buffer.WriteFloat(aoDepth_);
+            buffer.WriteUInt(nsamples_);
+            buffer.WriteFloat(aoDepth_);
+            buffer.WriteFloat(aoMin_);
+            buffer.WriteFloat(aoMultiply_);
+        }
+
+        void Unpack(VectorBuffer& buffer)
+        {
+            lightmapSize_ = buffer.ReadInt();
+            outputFormat_ = (GlowOutputFormat) buffer.ReadUInt();
+
+            lexelDensity_ = buffer.ReadFloat();
+            sceneLexelDensityScale_ = buffer.ReadFloat();
+
+            giEnabled_ = buffer.ReadBool();
+            giGranularity_ = buffer.ReadInt();
+            giMaxBounces_ = buffer.ReadInt();
+
+            aoEnabled_ = buffer.ReadBool();
+            aoDepth_ = buffer.ReadFloat();
+            nsamples_ = buffer.ReadUInt( );
+            aoDepth_= buffer.ReadFloat();
+            aoMin_ = buffer.ReadFloat();
+            aoMultiply_ = buffer.ReadFloat();
+        }
+
+        void Validate()
+        {
+            // always use 2048 for lightmap size
+            lightmapSize_ = 2048;
+
+            sceneLexelDensityScale_ = Atomic::Clamp<float>(sceneLexelDensityScale_, 0.01f, 1.0f);
+
+            lexelDensity_ = Atomic::Clamp<float>(lexelDensity_, 0.01f, 1.0f);
+            nsamples_ = Atomic::Clamp<unsigned>(nsamples_, 16, 256);
+
+            giMaxBounces_ = Atomic::Clamp<int>(giMaxBounces_, 0, 8);
+            giGranularity_ = Atomic::Clamp<int>(giGranularity_, 4, 16);
+
+            aoDepth_ = Atomic::Clamp<float>(aoDepth_, 0.01f, 10.0f);
+            aoMin_ = Atomic::Clamp<float>(aoMin_, 0.0f, 0.95f);
+            aoMultiply_ = Atomic::Clamp<float>(aoMultiply_, 0.01f, 100.0f);
+        }
+
+        void SetDefaults(GlowPreset preset = GLOW_PRESET_FAST_LOW_QUALITY)
+        {
+            // fix me
+            projectPath_ = "/Users/jenge/Dev/atomic/AtomicExamplesPrivate/AtomicGlowTests/TestScene1/";
+
+            // common settings
+
+            // lightmap size
+            lightmapSize_ = 2048;
+
+            giMaxBounces_ = 3;
+
+            sceneLexelDensityScale_ = 0.35f;
+
+            // TODO: Factor in DDS scene lighting loader, which have tested
+            // and minimal artifacts with significant runtime memory savings
+            outputFormat_ = GLOW_OUTPUT_PNG;
+
+            aoEnabled_ = false;
+            aoDepth_ = 0.25f;
+            aoMin_ = 0.45f;
+            aoMultiply_ = 1.0f;
+
+            switch (preset)
+            {
+                case GLOW_PRESET_FAST_LOW_QUALITY:
+                    lexelDensity_ = 0.16f;
+                    nsamples_ = 16;
+                    giEnabled_ = false;
+                    break;
+                case GLOW_PRESET_MEDIUM_QUALITY:
+                    lexelDensity_ = 0.32f;
+                    nsamples_ = 64;
+                    giEnabled_ = true;
+                    giGranularity_ = 8;
+                    break;
+                case GLOW_PRESET_HIGH_QUALITY:
+                    lexelDensity_ = 0.5f;                    
+                    giEnabled_ = true;
+                    nsamples_ = 256;
+                    giGranularity_ = 8;
+                    break;
+                case GLOW_PRESET_SLOW_EXTREME_QUALITY:
+                    lexelDensity_ = 0.65f;
+                    nsamples_ = 256;
+                    giEnabled_ = true;
+                    giGranularity_ = 8;
+                    break;
+            }
+        }
+
+    };
+
+    extern GlowSettings GlobalGlowSettings;
+
+}

+ 314 - 0
Source/AtomicGlow/GlowApplication/AtomicGlowApp.cpp

@@ -0,0 +1,314 @@
+//
+// Copyright (c) 2008-2014 the Urho3D project.
+// 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/CoreEvents.h>
+#include <Atomic/Core/WorkQueue.h>
+#include <Atomic/Engine/EngineDefs.h>
+#include <Atomic/Engine/Engine.h>
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/IOEvents.h>
+
+#include <Atomic/Resource/ResourceMapRouter.h>
+#include <Atomic/IPC/IPCEvents.h>
+
+#include <ToolCore/ToolSystem.h>
+#include <ToolCore/ToolEnvironment.h>
+
+#include <AtomicGlow/Common/GlowSettings.h>
+#include <AtomicGlow/Common/GlowEvents.h>
+#include <AtomicGlow/Kernel/BakeModel.h>
+#include <AtomicGlow/Kernel/BakeMaterial.h>
+#include <AtomicGlow/Kernel/SceneBaker.h>
+
+#include "AtomicGlowApp.h"
+
+using namespace ToolCore;
+
+#ifdef ATOMIC_PLATFORM_OSX
+#include <unistd.h>
+#endif
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+#include <stdio.h>
+#endif
+
+
+ATOMIC_DEFINE_APPLICATION_MAIN(AtomicGlow::AtomicGlowApp);
+
+namespace AtomicGlow
+{
+
+    AtomicGlowApp::AtomicGlowApp(Context* context) :
+        IPCClientApp(context)
+    {
+
+    }
+
+    AtomicGlowApp::~AtomicGlowApp()
+    {
+
+    }
+
+    void AtomicGlowApp::HandleIPCCmd(StringHash eventType, VariantMap& eventData)
+    {
+
+        using namespace IPCCmd;
+
+        IPC* ipc = GetSubsystem<IPC>();
+
+        String cmd = eventData[P_COMMAND].GetString().ToLower();
+        unsigned id = eventData[P_ID].GetUInt();
+
+        VariantMap result;
+        result[P_COMMAND] = cmd;
+        result[P_ID] = id;
+
+        if (cmd == "bake")
+        {
+
+            timer_.Reset();
+
+            // settings
+            VectorBuffer settingsBuffer = eventData["settings"].GetVectorBuffer();
+            GlobalGlowSettings.Unpack(settingsBuffer);
+
+            ATOMIC_LOGINFO("AtomicGlow bake received, baking");
+
+            sceneBaker_->SetStandaloneMode(false);
+            sceneBaker_->LoadScene("Scenes/Scene.scene");
+
+            using namespace IPCCmdResult;
+            result["result"] = "success";
+
+            ipc->SendEventToBroker(E_IPCCMDRESULT, result);
+
+        }
+
+        if (cmd == "quit")
+        {
+            ATOMIC_LOGINFO("AtomicGlow quit received, exiting");
+            exitCode_ = EXIT_SUCCESS;
+            engine_->Exit();
+        }
+
+    }
+
+    void AtomicGlowApp::HandleLogMessage(StringHash eventType, VariantMap& eventData)
+    {
+        using namespace LogMessage;
+
+        if (GetBrokerActive())
+        {
+
+            if (!GetIPC())
+                return;
+
+            VariantMap logEvent;
+            logEvent[IPCWorkerLog::P_LEVEL] = eventData[P_LEVEL].GetInt();
+            logEvent[IPCWorkerLog::P_MESSAGE] = eventData[P_MESSAGE].GetString();
+            GetIPC()->SendEventToBroker(E_IPCWORKERLOG, logEvent);
+        }
+
+    }
+
+
+    void AtomicGlowApp::HandleUpdate(StringHash eventType, VariantMap& eventData)
+    {
+        // if no scene has been loaded, return
+        if (!sceneBaker_->GetScene())
+        {
+            return;
+        }
+
+        if (!GetSubsystem<WorkQueue>()->IsCompleted(M_MAX_UNSIGNED))
+        {
+            return;
+        }
+
+        // if we're done, exit in standalone mode, or send IPC event if
+        // running off Glow service
+        if (sceneBaker_->GetCurrentLightMode() == GLOW_LIGHTMODE_COMPLETE)
+        {
+            String resultString = ToString("Scene lit in %i seconds", (int) (timer_.GetMSec(false) / 1000.0f));
+
+            ATOMIC_LOGINFO(resultString);
+
+            UnsubscribeFromEvent(E_UPDATE);
+
+            sceneBaker_->GenerateLightmaps();
+
+            if (sceneBaker_->GetStandaloneMode())
+            {
+                // TODO: write scene file/lightmaps in standalone mode
+
+                exitCode_ = EXIT_SUCCESS;
+                engine_->Exit();
+                return;
+            }
+            else
+            {
+                using namespace AtomicGlowResult;
+                VariantMap eventData;
+                eventData[P_SUCCESS] = true;
+                eventData[P_RESULT] = resultString;
+                eventData[P_BAKEDATA] = sceneBaker_->GetBakeData();
+                GetIPC()->SendEventToBroker(E_ATOMICGLOWRESULT, eventData);
+            }
+
+            return;
+        }
+
+        if (sceneBaker_->GetCurrentLightMode() == GLOW_LIGHTMODE_UNDEFINED)
+        {
+
+            // light mode will either move to direct or complete, depending on work to do
+            sceneBaker_->Light(GLOW_LIGHTMODE_DIRECT);
+            return;
+        }
+
+        if (sceneBaker_->GetCurrentLightMode() == GLOW_LIGHTMODE_DIRECT)
+        {
+            sceneBaker_->LightFinishCycle();
+
+            // light mode will either move to indirect or complete, depending on work to do
+            sceneBaker_->Light(GLOW_LIGHTMODE_INDIRECT);
+            return;
+        }
+
+        if (sceneBaker_->GetCurrentLightMode() == GLOW_LIGHTMODE_INDIRECT)
+        {
+            sceneBaker_->LightFinishCycle();
+
+            // light mode will either move to indirect or complete, depending on work to do
+            sceneBaker_->Light(GLOW_LIGHTMODE_INDIRECT);
+            return;
+        }
+
+    }
+
+    void AtomicGlowApp::Start()
+    {
+        if (exitCode_)
+            return;
+
+        // Initialize resource mapper
+        SharedPtr<ResourceMapRouter> router(new ResourceMapRouter(context_, "__atomic_ResourceCacheMap.json"));
+
+        if (!engine_->Initialize(engineParameters_))
+        {
+            ErrorExit();
+            return;
+        }
+
+        context_->RegisterSubsystem(new BakeMaterialCache(context_));
+        context_->RegisterSubsystem(new BakeModelCache(context_));
+
+        SubscribeToEvent(E_UPDATE, ATOMIC_HANDLER(AtomicGlowApp, HandleUpdate));
+        SubscribeToEvent(E_LOGMESSAGE, ATOMIC_HANDLER(AtomicGlowApp, HandleLogMessage));
+        SubscribeToEvent(E_IPCMESSAGE, ATOMIC_HANDLER(AtomicGlowApp, HandleUpdate));
+        SubscribeToEvent(E_IPCCMD, ATOMIC_HANDLER(AtomicGlowApp, HandleIPCCmd));
+
+        bool ipc = IPCClientApp::Initialize(arguments_);
+
+        sceneBaker_ = new SceneBaker(context_);
+
+        if (!ipc)
+        {
+            // TODO: proper standalone scene arg
+            sceneBaker_->LoadScene("Scenes/Scene.scene");
+        }
+
+    }
+
+    void AtomicGlowApp::Setup()
+    {
+        // AtomicGlow is always headless
+        engineParameters_["Headless"] = true;
+
+        FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
+        ToolSystem* tsystem = new ToolSystem(context_);
+        context_->RegisterSubsystem(tsystem);
+
+        ToolEnvironment* env = new ToolEnvironment(context_);
+        context_->RegisterSubsystem(env);
+
+        // Initialize the ToolEnvironment
+        if (!env->Initialize(true))
+        {
+            ErrorExit("Unable to initialize tool environment");
+            return;
+        }
+
+        String projectPath;
+
+        for (unsigned i = 0; i < arguments_.Size(); ++i)
+        {
+            if (arguments_[i].Length() > 1)
+            {
+                String argument = arguments_[i].ToLower();
+                String value = i + 1 < arguments_.Size() ? arguments_[i + 1] : String::EMPTY;
+
+                if (argument == "--project" && value.Length())
+                {
+                    if (GetExtension(value) == ".atomic")
+                    {
+                        value = GetPath(value);
+                    }
+
+                    if (fileSystem->DirExists(value))
+                    {
+
+                    }
+                    else
+                    {
+                        ErrorExit(ToString("%s project path does not exist", value.CString()));
+                    }
+
+                    projectPath = AddTrailingSlash(value);
+
+                }
+            }
+        }
+
+        engineParameters_.InsertNew("LogName", fileSystem->GetAppPreferencesDir("AtomicEditor", "Logs") + "AtomicGlow.log");
+
+    #ifdef ATOMIC_DEV_BUILD
+        engineParameters_["ResourcePrefixPaths"] = env->GetRootSourceDir() + "/Resources/";
+        engineParameters_["ResourcePaths"] = ToString("CoreData;EditorData;%sResources;%sCache", projectPath.CString(), projectPath.CString());
+    #endif
+
+        // TODO: change to using new workerthreadcount command line arg
+        // which exposed in editor GlowComponent UI
+        // also, check how the number of threads affects light times
+        // engineParameters_[EP_WORKER_THREADS_COUNT] = 8;
+
+        IPCClientApp::Setup();
+
+    }
+
+
+}
+
+

+ 63 - 0
Source/AtomicGlow/GlowApplication/AtomicGlowApp.h

@@ -0,0 +1,63 @@
+//
+// Copyright (c) 2008-2014 the Urho3D project.
+// 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/Timer.h>
+#include <AtomicApp/IPCClientApp.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+    class SceneBaker;
+
+
+    class AtomicGlowApp : public IPCClientApp
+    {
+        ATOMIC_OBJECT(AtomicGlowApp, IPCClientApp)
+
+    public:
+        /// Construct.
+        AtomicGlowApp(Context* context);
+        virtual ~AtomicGlowApp();
+
+    private:
+
+        void HandleUpdate(StringHash eventType, VariantMap& eventData);
+        void HandleIPCCmd(StringHash eventType, VariantMap& eventData);
+        void HandleLogMessage(StringHash eventType, VariantMap& eventData);
+
+
+        /// Setup before engine initialization.
+        virtual void Setup();
+
+        virtual void Start();
+
+        Timer timer_;
+
+        SharedPtr<SceneBaker> sceneBaker_;
+
+    };
+
+}

+ 192 - 0
Source/AtomicGlow/GlowService/GlowProcess.cpp

@@ -0,0 +1,192 @@
+//
+// Copyright (c) 2014-2017 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.
+//
+
+#include <Atomic/IO/Log.h>
+#include <Atomic/Core/Context.h>
+#include <Atomic/IPC/IPCBroker.h>
+#include <Atomic/IPC/IPCEvents.h>
+
+#include "../Common/GlowEvents.h"
+
+#include "GlowService.h"
+#include "GlowServiceEvents.h"
+#include "GlowProcess.h"
+
+namespace AtomicGlow
+{
+
+
+// Atomic Glow Process
+GlowProcess::GlowProcess(Context* context) : IPCServer(context),
+    exitCalled_(false),
+    resultHandler_(new GlowProcessResultHandler(context, this))
+{
+
+}
+
+GlowProcess::~GlowProcess()
+{
+
+}
+
+void GlowProcess::OnIPCWorkerExited()
+{
+    GetSubsystem<GlowService>()->OnGlowProcessExited();
+}
+
+void GlowProcess::OnIPCWorkerLog(int level, const String& message)
+{
+    using namespace AtomicGlowServiceLogEvent;
+
+    VariantMap eventData;
+    eventData[P_LEVEL] = level;
+    eventData[P_MESSAGE] = message;
+
+    SendEvent(E_ATOMICGLOWSERVICELOGEVENT, eventData);
+}
+
+bool GlowProcess::Start(const String &glowBinaryPath, const StringVector &baseArgs)
+{
+    if (!IPCServer::StartInternal(glowBinaryPath, baseArgs))
+    {
+        return false;
+    }
+
+    SubscribeToEvent(GetServerBroker(), E_ATOMICGLOWRESULT, ATOMIC_HANDLER(GlowProcess, HandleAtomicGlowResult));
+
+    return true;
+}
+
+void GlowProcess::HandleAtomicGlowResult(StringHash eventType, VariantMap& eventData)
+{
+    GlowService* glowService = GetSubsystem<GlowService>();
+
+    using namespace AtomicGlowResult;
+
+    const String& result = eventData[P_RESULT].GetString();
+
+    if (!eventData[P_SUCCESS].GetBool())
+    {
+        glowService->OnBakeError(result);
+        return;
+    }
+
+    Variant variant;
+
+    if (!eventData.TryGetValue(P_BAKEDATA, variant) || variant.GetType() != VAR_BUFFER)
+    {
+        glowService->OnBakeError("GlowProcess::HandleResult() - Unable to get bake data");
+        return;
+    }
+
+    VectorBuffer bakeData = variant.GetVectorBuffer();
+
+    glowService->ProcessBakeData(bakeData);
+
+    glowService->OnBakeSuccess();
+
+}
+
+unsigned GlowProcess::QueueCommand(const VariantMap& cmdMap)
+{
+    return IPCServer::QueueCommand(resultHandler_, cmdMap);
+}
+
+void GlowProcess::Exit()
+{
+    if (exitCalled_)
+        return;
+
+    exitCalled_ = true;
+
+    using namespace IPCCmd;
+    VariantMap cmdMap;
+    cmdMap[P_COMMAND] = "quit";
+    QueueCommand(cmdMap);
+}
+
+void GlowProcess::HandleResult(unsigned cmdID, const VariantMap& cmdResult)
+{
+    using namespace IPCCmdResult;
+
+    GlowService* glowService = GetSubsystem<GlowService>();
+
+    Variant variant;
+    String cmd;
+    String result;
+
+    if (!cmdResult.TryGetValue(P_COMMAND, variant) || variant.GetType() != VAR_STRING)
+    {
+        ATOMIC_LOGERROR("GlowProcess::HandleResult() - Unable to process result, command key missing");
+        return;
+    }
+
+    cmd = variant.GetString();
+
+    if (!cmdResult.TryGetValue("result", variant) || variant.GetType() != VAR_STRING)
+    {
+        ATOMIC_LOGERROR("GlowProcess::HandleResult() - Unable to process result, command key missing");
+        return;
+    }
+
+    result = variant.GetString();
+
+    if (cmd == "bake")
+    {
+        if (result != "success")
+        {
+            glowService->OnBakeError(result);
+            return;
+        }
+    }
+
+}
+
+// Result Handler
+
+GlowProcessResultHandler::GlowProcessResultHandler(Context* context, GlowProcess* process) :
+    IPCResultHandler(context),
+    process_(process)
+{
+
+}
+
+GlowProcessResultHandler::~GlowProcessResultHandler()
+{
+
+}
+
+void GlowProcessResultHandler::HandleResult(unsigned cmdID, const VariantMap& cmdResult)
+{
+    if (!process_)
+    {
+        ATOMIC_LOGWARNING("lowProcessResultHandler::HandleResult() - called without current Glow process");
+        return;
+    }
+
+    process_->HandleResult(cmdID, cmdResult);
+
+}
+
+
+}
+

+ 88 - 0
Source/AtomicGlow/GlowService/GlowProcess.h

@@ -0,0 +1,88 @@
+//
+// Copyright (c) 2014-2017 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.
+//
+
+#include <Atomic/IPC/IPCServer.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+class GlowProcessResultHandler;
+
+class GlowProcess : public IPCServer
+{
+    friend class GlowProcessResultHandler;
+
+    ATOMIC_OBJECT(GlowProcess, IPCServer)
+
+public:
+
+    /// Construct.
+    GlowProcess(Context* context);
+    /// Destruct.
+    virtual ~GlowProcess();
+
+    bool Start() { return false; }
+
+    bool Start(const String& glowBinaryPath, const StringVector& baseArgs);
+
+    void Exit();
+
+    bool GetExitCalled() const { return exitCalled_; }
+
+    unsigned QueueCommand(const VariantMap& cmdMap);
+
+private:
+
+    void OnIPCWorkerLog(int level, const String& message);
+    void OnIPCWorkerExited();
+
+    void HandleResult(unsigned cmdID, const VariantMap& cmdResult);
+
+    void HandleAtomicGlowResult(StringHash eventType, VariantMap& eventData);
+
+    bool exitCalled_;
+
+    SharedPtr<GlowProcessResultHandler> resultHandler_;
+
+};
+
+class GlowProcessResultHandler : public IPCResultHandler
+{
+    ATOMIC_OBJECT(GlowProcessResultHandler, IPCResultHandler)
+
+public:
+    /// Construct.
+    GlowProcessResultHandler(Context* context, GlowProcess* process);
+    /// Destruct.
+    virtual ~GlowProcessResultHandler();
+
+    void HandleResult(unsigned cmdID, const VariantMap& cmdResult);
+
+private:    
+
+    WeakPtr<GlowProcess> process_;
+
+};
+
+}

+ 236 - 0
Source/AtomicGlow/GlowService/GlowService.cpp

@@ -0,0 +1,236 @@
+//
+// Copyright (c) 2014-2017 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.
+//
+
+#include <Atomic/IO/Log.h>
+#include <Atomic/IPC/IPCEvents.h>
+#include <Atomic/Graphics/StaticModel.h>
+
+
+#include "GlowProcess.h"
+#include "GlowServiceEvents.h"
+#include "GlowService.h"
+
+namespace AtomicGlow
+{
+
+GlowService::GlowService(Context* context) : Object(context),
+    bakeCanceled_(false)
+{
+
+}
+
+GlowService::~GlowService()
+{
+
+}
+
+void GlowService::OnGlowProcessExited()
+{
+    if (glowProcess_.NotNull())
+    {
+        if (!glowProcess_->GetExitCalled() || bakeCanceled_)
+        {
+            using namespace AtomicGlowServiceBakeResult;
+
+            VariantMap eventData;
+            eventData[P_RESULT] = bakeCanceled_ ? "Bake canceled" : "GlowService::OnGlowProcessExited() - Glow process exited unexpectedly";
+            eventData[P_SUCCESS] = false;
+            SendEvent(E_ATOMICGLOWSERVICEBAKERESULT, eventData);
+        }
+    }
+
+    glowProcess_ = 0;
+    scene_ = 0;
+
+}
+
+void GlowService::OnBakeError(const String& result)
+{
+    ATOMIC_LOGERRORF("%s", result.CString());
+    glowProcess_->Exit();
+
+    using namespace AtomicGlowServiceBakeResult;
+
+    VariantMap eventData;
+    eventData[P_RESULT] = result;
+    eventData[P_SUCCESS] = false;
+
+    SendEvent(E_ATOMICGLOWSERVICEBAKERESULT, eventData);
+}
+
+void GlowService::OnBakeSuccess()
+{
+    glowProcess_->Exit();
+
+    using namespace AtomicGlowServiceBakeResult;
+
+    VariantMap eventData;
+    eventData[P_RESULT] = "success";
+    eventData[P_SUCCESS] = true;
+
+    SendEvent(E_ATOMICGLOWSERVICEBAKERESULT, eventData);
+
+}
+
+void GlowService::ProcessBakeData(VectorBuffer& bakeData)
+{
+    if (!scene_)
+    {
+        ATOMIC_LOGERROR("GlowService::ProcessBakeData() - called with null scene");
+        return;
+    }
+
+    unsigned count = bakeData.ReadUInt();
+
+    PODVector<Node*> children;
+
+    scene_->GetChildrenWithComponent<StaticModel>(children, true);
+
+    for (unsigned i = 0; i < count; i++)
+    {
+        Node* node = 0;
+        StaticModel* staticModel = 0;
+
+        unsigned nodeID = bakeData.ReadUInt();
+        unsigned staticModelID = bakeData.ReadUInt();
+        unsigned lightMask = bakeData.ReadUInt();
+        unsigned lightmapIndex = bakeData.ReadUInt();
+        Vector4 tilingOffset = bakeData.ReadVector4();
+
+        for (unsigned j = 0; j < children.Size(); j++)
+        {
+            if (children[j]->GetID() == nodeID)
+            {
+                node = children[j];
+                staticModel = node->GetComponent<StaticModel>();
+
+                if (!staticModel || staticModel->GetID() != staticModelID)
+                {
+                    ATOMIC_LOGERROR("GlowService::ProcessBakeData() - mismatched node <-> static model ID");
+                    return;
+                }
+
+                break;
+            }
+
+        }
+
+        if (!node)
+        {
+            ATOMIC_LOGERROR("GlowService::ProcessBakeData() - unable to find node ID");
+            return;
+        }
+
+        staticModel->SetLightMask(lightMask);
+        staticModel->SetLightmapIndex(lightmapIndex);
+        staticModel->SetLightmapTilingOffset(tilingOffset);
+
+    }
+
+}
+
+bool GlowService::Bake(Scene* scene, const GlowSettings& settings)
+{
+    if (!scene)
+    {
+        ATOMIC_LOGERROR("GlowService::Bake() - Called with null scene");
+        return false;
+    }
+
+    if (glowProcess_.NotNull())
+    {
+        ATOMIC_LOGERROR("GlowService::Bake() - Called with existing glow process");
+        return false;
+    }
+
+    bakeCanceled_ = false;
+    result_.Clear();
+
+    glowProcess_ = new GlowProcess(context_);
+
+    if (!glowProcess_->Start(glowBinaryPath_, glowBaseArgs_))
+    {
+        ATOMIC_LOGERROR("GlowService::Bake() - Glow process failed to start");
+        return false;
+    }
+
+    scene_ = scene;
+
+    using namespace IPCCmd;
+    VariantMap cmdMap;
+    cmdMap[P_COMMAND] = "bake";
+
+    // settings
+    VectorBuffer settingsBuffer;
+    settings.Pack(settingsBuffer);
+    cmdMap["settings"] = settingsBuffer;
+
+    glowProcess_->QueueCommand(cmdMap);
+
+    return true;
+
+}
+
+void GlowService::CancelBake()
+{
+
+    if (glowProcess_.Null())
+    {
+        ATOMIC_LOGERROR("GlowService::CancelBake() - Called without existing glow process");
+        return;
+    }
+
+    bakeCanceled_ = true;
+    glowProcess_->Exit();
+
+}
+
+
+bool GlowService::Start()
+{
+    bool failed = false;
+
+    if (failed)
+    {
+        ATOMIC_LOGERROR("GlowService::Start() - Unable to start AtomicGlow service");
+        return false;
+    }
+
+    glowBinaryPath_ = String::EMPTY;
+    glowBaseArgs_.Clear();
+
+#ifdef ATOMIC_DEBUG
+    glowBinaryPath_ = "/Users/jenge/Dev/atomic/build-AtomicGameEngine-Desktop-Debug/Source/AtomicGlow/AtomicGlow";
+#else
+    glowBinaryPath_ = "/Users/jenge/Dev/atomic/build-AtomicGameEngine-Desktop-Release/Source/AtomicGlow/AtomicGlow";
+#endif
+
+    glowBaseArgs_.Push("--project");
+    glowBaseArgs_.Push("/Users/jenge/Dev/atomic/AtomicExamplesPrivate/AtomicGlowTests/TestScene1");
+
+    return true;
+
+}
+
+
+
+}

+ 75 - 0
Source/AtomicGlow/GlowService/GlowService.h

@@ -0,0 +1,75 @@
+//
+// Copyright (c) 2014-2017 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.
+//
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+#include <Atomic/Scene/Scene.h>
+
+#include <AtomicGlow/Common/GlowSettings.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+class GlowProcess;
+
+class GlowService : public Object
+{
+    friend class GlowProcess;
+
+    ATOMIC_OBJECT(GlowService, Object)
+
+public:
+    /// Construct.
+    GlowService(Context* context);
+    /// Destruct.
+    virtual ~GlowService();
+
+    bool Start();
+
+    bool Bake(Scene* scene, const GlowSettings& settings);
+
+    void CancelBake();
+
+private:
+
+    void OnBakeError(const String& result);
+    void OnBakeSuccess();
+
+    void ProcessBakeData(VectorBuffer& bakeData);
+
+    void OnGlowProcessExited();
+
+    String glowBinaryPath_;
+    StringVector glowBaseArgs_;
+
+    WeakPtr<Scene> scene_;
+    SharedPtr<GlowProcess> glowProcess_;
+
+    String result_;
+    bool bakeCanceled_;
+
+};
+
+}

+ 44 - 0
Source/AtomicGlow/GlowService/GlowServiceEvents.h

@@ -0,0 +1,44 @@
+//
+// 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.
+//
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+ATOMIC_EVENT(E_ATOMICGLOWSERVICEBAKERESULT, AtomicGlowServiceBakeResult)
+{
+    ATOMIC_PARAM(P_SUCCESS, Success);    // bool
+    ATOMIC_PARAM(P_RESULT, Result);    // String
+}
+
+ATOMIC_EVENT(E_ATOMICGLOWSERVICELOGEVENT, AtomicGlowServiceLogEvent)
+{
+    ATOMIC_PARAM(P_LEVEL, Level);    // bool
+    ATOMIC_PARAM(P_MESSAGE, Message);    // String
+}
+
+}

+ 477 - 0
Source/AtomicGlow/Kernel/BakeLight.cpp

@@ -0,0 +1,477 @@
+// Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
+// Copyright 2009-2017 Intel Corporation
+//
+// 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/IO/Log.h>
+#include <Atomic/Graphics/Zone.h>
+
+#include <AtomicGlow/Common/GlowSettings.h>
+
+#include "EmbreeScene.h"
+
+#include "LightRay.h"
+#include "BakeLight.h"
+#include "BakeMesh.h"
+#include "SceneBaker.h"
+
+namespace AtomicGlow
+{
+
+const float LIGHT_ANGLE_EPSILON = 0.001f;
+
+// http://www.altdevblogaday.com/2012/05/03/generating-uniformly-distributed-points-on-sphere/
+static inline void GetRandomDirection(Vector3& result)
+{
+    float z = 2.0f * rand() / RAND_MAX - 1.0f;
+    float t = 2.0f * rand() / RAND_MAX * 3.14f;
+    float r = sqrt(1.0f - z * z);
+    result.x_ = r * (float) cos(t);
+    result.y_ = r * (float) sin(t);
+    result.z_ = z;
+}
+
+BakeLight::BakeLight(Context* context, SceneBaker* sceneBaker) : BakeNode(context, sceneBaker),
+    range_(0.0f)
+{
+}
+
+BakeLight::~BakeLight()
+{
+
+}
+
+// Zone Lights
+
+ZoneBakeLight::ZoneBakeLight(Context* context, SceneBaker* sceneBaker) : BakeLight(context, sceneBaker)
+{
+}
+
+ZoneBakeLight::~ZoneBakeLight()
+{
+
+}
+
+void ZoneBakeLight::Light(LightRay* lightRay)
+{
+    LightRay::SamplePoint& source = lightRay->samplePoint_;
+
+    if (source.normal == Vector3::ZERO)
+        return;
+
+    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
+
+    const Color& color = zone_->GetAmbientColor();
+
+    Vector3 rad(color.r_, color.g_, color.b_);
+
+    if (!GlobalGlowSettings.aoEnabled_)
+    {
+        source.bakeMesh->ContributeRadiance(lightRay, rad, GLOW_LIGHTMODE_AMBIENT);
+        return;
+    }
+
+
+    // TODO: AO using ray packets/streams
+
+    RTCRay& ray = lightRay->rtcRay_;
+
+    unsigned nsamples = GlobalGlowSettings.nsamples_;
+
+    // this needs to be based on model/scale likely?
+    float aoDepth = GlobalGlowSettings.aoDepth_;
+
+    // smallest percent of ao value to use
+    float aoMin = GlobalGlowSettings.aoMin_;
+
+    // brightness control
+    float multiply = GlobalGlowSettings.aoMultiply_;
+
+    // Shoot rays through the differential hemisphere.
+    int nhits = 0;
+    float avgDepth = 0.0f;
+    for (unsigned nsamp = 0; nsamp < nsamples; nsamp++)
+    {
+        Vector3 rayDir;
+        GetRandomDirection(rayDir);
+
+        float dotp = source.normal.x_ * rayDir.x_ +
+                     source.normal.y_ * rayDir.y_ +
+                     source.normal.z_ * rayDir.z_;
+
+        if (dotp < 0.1f)
+        {
+            continue;
+        }
+
+        float variance = 0.0f;//nsamples <= 32 ? 0.0f : aoDepth * ((float) rand() / (float) RAND_MAX) * 0.25f;
+
+        float depth = aoDepth + variance;
+
+        lightRay->SetupRay(source.position, rayDir, .001f, depth);
+
+        rtcOccluded(scene, ray);
+
+        if (ray.geomID != RTC_INVALID_GEOMETRY_ID)
+        {
+            avgDepth += Min<float>(ray.tfar, aoDepth);
+            nhits++;
+        }
+    }
+
+    if (nhits)// && (nsamples <= 32 ? true : nhits > 4))
+    {
+        avgDepth /= float(nhits);
+        avgDepth /= aoDepth;
+
+        avgDepth = Clamp<float>(avgDepth, 0.1f, 1.0f) * 100.0f;
+        avgDepth *= avgDepth;
+        float ao = avgDepth / 10000.0f;
+
+        ao = aoMin + ao/2.0f;
+        ao *= multiply;
+        ao = Clamp<float>(ao, aoMin, 1.0f);
+
+        rad *= ao;
+    }
+
+    source.bakeMesh->ContributeRadiance(lightRay, rad, GLOW_LIGHTMODE_AMBIENT);
+}
+
+void ZoneBakeLight::SetZone(Zone* zone)
+{
+    node_ = zone->GetNode();
+    zone_ = zone;
+}
+
+
+// Directional Lights
+
+DirectionalBakeLight::DirectionalBakeLight(Context* context, SceneBaker* sceneBaker) : BakeLight(context, sceneBaker)
+{
+}
+
+DirectionalBakeLight::~DirectionalBakeLight()
+{
+
+}
+
+void DirectionalBakeLight::Light(LightRay* lightRay)
+{
+    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
+
+    LightRay::SamplePoint& source = lightRay->samplePoint_;
+    RTCRay& ray = lightRay->rtcRay_;
+
+    float angle = direction_.DotProduct(source.normal);
+
+    if (angle < 0.0f)
+        return;
+
+    lightRay->SetupRay(source.position, direction_);
+
+    rtcOccluded(scene, ray);
+
+    // obstructed?  TODO: glass, etc
+    if (ray.geomID != RTC_INVALID_GEOMETRY_ID)
+        return;
+
+    Vector3 rad(color_.r_, color_.g_, color_.b_);
+
+    rad*=angle;
+
+    source.bakeMesh->ContributeRadiance(lightRay, rad);
+
+}
+
+void DirectionalBakeLight::SetLight(Atomic::Light* light)
+{
+    node_ = light->GetNode();
+
+    color_ = light->GetColor();
+
+    direction_ = -node_->GetWorldDirection();
+    direction_.Normalize();
+
+}
+
+// Point Lights
+
+PointBakeLight::PointBakeLight(Context* context, SceneBaker* sceneBaker) : BakeLight(context, sceneBaker)
+{
+}
+
+PointBakeLight::~PointBakeLight()
+{
+
+}
+
+void PointBakeLight::Light(LightRay* lightRay)
+{
+    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
+
+    LightRay::SamplePoint& source = lightRay->samplePoint_;
+    RTCRay& ray = lightRay->rtcRay_;
+
+    Vector3 dir = position_ - source.position;
+
+    float dist = dir.Length();
+
+    if (range_ <= 0.0f || dist >= range_)
+        return;
+
+    dir.Normalize();
+
+    float dot = dir.DotProduct(source.normal);
+
+    if (dot < 0.0f)
+        return;
+
+    lightRay->SetupRay(source.position, dir, .001f, dist);
+
+    rtcOccluded(scene, ray);
+
+    // obstructed?  TODO: glass, etc
+    if (ray.geomID != RTC_INVALID_GEOMETRY_ID)
+        return;
+
+    Vector3 rad(color_.r_, color_.g_, color_.b_);
+
+    // lightOverBright 1.2 for example will pop light,
+    // needs to be configurable per light, maybe with brightness modifer
+    float lightOverBright = 1.0f;
+
+    rad *= Max<float> (1.0f - ( dist * lightOverBright / range_), 0.0f);
+    rad *= dot;
+
+    // EXPERIMENTAL: if GI is enabled, dim point light a bit
+
+    if (GlobalGlowSettings.giEnabled_)
+    {
+        rad *= 0.75f;
+    }
+
+
+
+    if (rad.Length() > M_EPSILON)
+        source.bakeMesh->ContributeRadiance(lightRay, rad);
+
+}
+
+void PointBakeLight::SetLight(Atomic::Light* light)
+{
+    node_ = light->GetNode();
+
+    color_ = light->GetColor();
+    position_ = node_->GetWorldPosition();
+
+    range_ = light->GetRange();
+
+}
+
+// Bounce Lights
+
+Mutex BounceBakeLight::sortMutex_;
+
+BounceBakeLight::BounceBakeLight(Context* context, SceneBaker* sceneBaker) : BakeLight(context, sceneBaker)
+{
+}
+
+BounceBakeLight::~BounceBakeLight()
+{
+
+}
+
+static Vector3 compareBouncePoint;
+static inline bool CompareBounceSamples(const BounceSample* lhs, const BounceSample* rhs)
+{
+    Vector3 v1 =  lhs->position_ - compareBouncePoint;
+    Vector3 v2 =  rhs->position_ - compareBouncePoint;
+    return v1.LengthSquared() < v2.LengthSquared();
+}
+
+void BounceBakeLight::Light(LightRay* lightRay)
+{
+    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
+    LightRay::SamplePoint& source = lightRay->samplePoint_;
+    RTCRay& ray = lightRay->rtcRay_;
+
+    const float maxDist = 5.0f;
+    const float maxDistSq = maxDist * maxDist;
+
+    const BounceSample* b;
+    PODVector<const BounceSample*> samples;
+
+    for (int i = 0; i < bounceSamples_.Size(); i++)
+    {
+        b = &bounceSamples_[i];
+
+        // don't light self
+        if (source.bakeMesh == bakeMesh_)
+        {
+            for (int j = 0; j < GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES; j++)
+            {
+                if (b->triIndex_[j] == -1)
+                    break;
+
+                if (b->triIndex_[j] == source.triangle)
+                    return;
+
+            }
+        }
+
+        Vector3 dir =  b->position_ - source.position;
+
+        if (dir.LengthSquared() > maxDistSq)
+        {
+            continue;
+        }
+
+        dir.Normalize();
+
+        if (dir.DotProduct(source.normal) < M_EPSILON)
+        {
+            continue;
+        }
+
+        samples.Push(b);
+
+    }
+
+    if (!samples.Size())
+        return;
+
+    sortMutex_.Acquire();
+
+    compareBouncePoint = source.position;
+
+    Sort(samples.Begin(), samples.End(), CompareBounceSamples);
+
+    sortMutex_.Release();
+
+
+    int bestIndex = -1;
+    float bestDist = M_INFINITY;
+    for (unsigned i = 0; i < samples.Size(); i++)
+    {
+        b = samples[i];
+
+        Vector3 dir = b->position_ - source.position;
+        float dist = dir.Length();
+
+        if (dist < bestDist)
+        {
+            dir.Normalize();
+
+            lightRay->SetupRay(source.position, dir, 0.01f, dist * 1.01f);
+
+            rtcIntersect(scene, ray);
+
+            if (ray.geomID != bakeMesh_->GetGeomID())
+            {
+                continue;
+            }
+
+            // backface
+            if ( (ray.Ng[0] * ray.dir[0]) +
+                 (ray.Ng[1] * ray.dir[1]) +
+                 (ray.Ng[2] * ray.dir[2]) < 0.0f)
+            {
+                continue;
+            }
+
+            bool found = false;
+            for (int j = 0; j < GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES; j++)
+            {
+                if (b->triIndex_[j] == -1)
+                    break;
+
+                if (b->triIndex_[j] == ray.primID)
+                {
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found)
+                continue;
+
+            bestIndex = i;
+            bestDist = dist;
+
+            break;
+        }
+
+    }
+
+    if (bestIndex == -1)
+        return;
+
+    b = samples[bestIndex];
+
+    // weighted average of src color and radiance for bounce sample
+    Vector3 rad = b->srcColor_ + (b->radiance_/b->hits_);
+    rad /= 2.0f;
+
+    float d = 1.0f - Clamp<float>(bestDist / maxDist, 0.01f, 1.0f);
+
+    rad *= d;
+
+    rad *= 0.15f;
+
+    if (rad.x_ < 0.0f || rad.y_ < 0.0f || rad.z_ < 0.0f)
+    {
+        ATOMIC_LOGWARNING("BounceBakeLight::Light - negative rad factor");
+        return;
+    }
+
+    source.bakeMesh->ContributeRadiance(lightRay, rad, GLOW_LIGHTMODE_INDIRECT);
+
+}
+
+void BounceBakeLight::SetBounceSamples(const PODVector<BounceSample>& bounceSamples)
+{
+    bounceSamples_ = bounceSamples;
+}
+
+void BounceBakeLight::SetBakeMesh(BakeMesh* bakeMesh)
+{
+    bakeMesh_ = bakeMesh;
+    node_ = bakeMesh->GetNode();
+
+    color_ = Color::MAGENTA;
+    range_ = -1.0f;
+    position_ = Vector3::ZERO;
+}
+
+void BounceBakeLight::SetLight(Atomic::Light* light)
+{
+    node_ = light->GetNode();
+
+    color_ = light->GetColor();
+    position_ = node_->GetWorldPosition();
+
+    range_ = light->GetRange();
+
+}
+
+
+
+}

+ 171 - 0
Source/AtomicGlow/Kernel/BakeLight.h

@@ -0,0 +1,171 @@
+// Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
+// Copyright 2009-2017 Intel Corporation
+//
+// 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/Mutex.h>
+#include "BakeNode.h"
+
+namespace Atomic
+{
+    class Light;
+    class Zone;
+}
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+class LightRay;
+class BakeMesh;
+
+class BakeLight : public BakeNode
+{
+    ATOMIC_OBJECT(BakeLight, BakeNode)
+
+    public:
+
+    BakeLight(Context* context, SceneBaker *sceneBaker);
+    virtual ~BakeLight();
+
+    virtual void SetLight(Atomic::Light* light) = 0;
+
+    virtual void Light(LightRay* ray) = 0;
+
+protected:
+
+    // Common light properties
+    Color color_;
+    Vector3 position_;
+    Vector3 direction_;
+
+    float range_;
+
+
+private:
+
+};
+
+// Zone ambient, etc
+class ZoneBakeLight : public BakeLight
+{
+    ATOMIC_OBJECT(ZoneBakeLight, BakeLight)
+
+    public:
+
+    ZoneBakeLight(Context* context, SceneBaker* sceneBaker);
+    virtual ~ZoneBakeLight();
+
+    void Light(LightRay* lightRay);
+    void SetLight(Atomic::Light* light) {}
+
+    void SetZone(Zone* zone);
+
+protected:
+
+private:
+
+    Zone* zone_;
+
+};
+
+
+class DirectionalBakeLight : public BakeLight
+{
+    ATOMIC_OBJECT(DirectionalBakeLight, BakeLight)
+
+    public:
+
+    DirectionalBakeLight(Context* context, SceneBaker* sceneBaker);
+    virtual ~DirectionalBakeLight();
+
+    void Set(const Vector3& direction, const Vector3& radiance, float cosAngle);
+
+    void Light(LightRay* lightRay);
+
+    void SetLight(Atomic::Light* light);
+
+
+protected:
+
+
+private:
+
+};
+
+class PointBakeLight : public BakeLight
+{
+    ATOMIC_OBJECT(PointBakeLight, BakeLight)
+
+    public:
+
+    PointBakeLight(Context* context, SceneBaker* sceneBaker);
+    virtual ~PointBakeLight();
+
+    void Light(LightRay* lightRay);
+
+    void SetLight(Atomic::Light* light);
+
+protected:
+
+
+private:
+
+};
+
+// BakeMesh Bounce Lighting
+class BounceBakeLight : public BakeLight
+{
+    ATOMIC_OBJECT(BounceBakeLight, BakeLight)
+
+    public:
+
+    BounceBakeLight(Context* context, SceneBaker* sceneBaker);
+    virtual ~BounceBakeLight();
+
+    void Light(LightRay* lightRay);
+
+    void SetLight(Atomic::Light* light);
+
+    void SetBakeMesh(BakeMesh* bakeMesh);
+
+    void SetBounceSamples(const PODVector<BounceSample>& bounceSamples);
+
+    unsigned GetNumBounceSamples() const { return bounceSamples_.Size(); }
+
+protected:
+
+
+private:
+
+    static Mutex sortMutex_;
+
+    PODVector<BounceSample> bounceSamples_;
+
+    WeakPtr<BakeMesh> bakeMesh_;
+
+};
+
+
+
+}

+ 167 - 0
Source/AtomicGlow/Kernel/BakeMaterial.cpp

@@ -0,0 +1,167 @@
+// Copyright (c) 2014-2017, 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/IO/Log.h>
+#include <Atomic/Resource/ResourceCache.h>
+#include <Atomic/Resource/XMLFile.h>
+#include <Atomic/Graphics/Material.h>
+
+#include "BakeMaterial.h"
+
+namespace AtomicGlow
+{
+
+
+BakeMaterialCache::BakeMaterialCache(Context* context) : Object(context)
+{
+}
+
+BakeMaterialCache::~BakeMaterialCache()
+{
+
+}
+
+BakeMaterial* BakeMaterialCache::GetBakeMaterial(Material *material)
+{
+    if (!material)
+        return 0;
+
+    SharedPtr<BakeMaterial> bakeMaterial;
+
+    if (bakeCache_.TryGetValue(material->GetName(), bakeMaterial))
+    {
+        return bakeMaterial;
+    }
+
+    bakeMaterial = new BakeMaterial(context_);
+
+    if (!bakeMaterial->LoadMaterial(material))
+    {
+        return 0;
+    }
+
+    bakeCache_[material->GetName()] = bakeMaterial;
+
+    return bakeMaterial;
+
+}
+
+BakeMaterial::BakeMaterial(Context* context) : Object(context),
+    occlusionMasked_(false)
+{
+    uoffset_ = Vector4(1.0f, 0.0f, 0.0f, 0.0f);
+    voffset_ = Vector4(0.0f, 1.0f, 0.0f, 0.0f);
+}
+
+BakeMaterial::~BakeMaterial()
+{
+
+}
+
+Variant BakeMaterial::ParseShaderParameterValue(const String& value)
+{
+    String valueTrimmed = value.Trimmed();
+    if (valueTrimmed.Length() && IsAlpha((unsigned)valueTrimmed[0]))
+        return Variant(ToBool(valueTrimmed));
+    else
+        return ToVectorVariant(valueTrimmed);
+}
+
+bool BakeMaterial::LoadMaterial(Material *material)
+{
+    material_ = material;
+
+    ATOMIC_LOGINFOF("Material: %s", material->GetName().CString());
+
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    XMLFile* xmlFile = cache->GetResource<XMLFile>(material->GetName());
+
+    if (!xmlFile)
+        return false;
+
+    XMLElement rootElem = xmlFile->GetRoot();
+
+    XMLElement techniqueElem = rootElem.GetChild("technique");
+
+    if (techniqueElem)
+    {
+        String name = techniqueElem.GetAttribute("name").ToLower();
+
+        // TODO: better way of setting/detecting occlusion masked materials
+        if (name.Contains("diffalpha") || name.Contains("difflightmapalpha"))
+        {
+            occlusionMasked_ = true;
+        }
+    }
+
+    // techniques
+
+    // textures
+    XMLElement textureElem = rootElem.GetChild("texture");
+    while (textureElem)
+    {
+        String unit = textureElem.GetAttribute("unit").ToLower();
+
+        if (unit == "diffuse")
+        {
+            String name = textureElem.GetAttribute("name");
+
+            diffuseTexture_ = cache->GetResource<Image>(name);
+
+            if (diffuseTexture_.Null())
+                return false;
+
+            ATOMIC_LOGINFOF("diffuse: %s %ux%u", name.CString(), diffuseTexture_->GetWidth(), diffuseTexture_->GetHeight());
+        }
+
+        textureElem = textureElem.GetNext("texture");
+    }
+
+    // Shader parameters
+
+    XMLElement parameterElem = rootElem.GetChild("parameter");
+    while (parameterElem)
+    {
+        String name = parameterElem.GetAttribute("name").ToLower();
+
+        Variant value;
+
+        if (!parameterElem.HasAttribute("type"))
+            value = ParseShaderParameterValue(parameterElem.GetAttribute("value"));
+        else
+            value = Variant(parameterElem.GetAttribute("type"), parameterElem.GetAttribute("value"));
+
+        if (name == "uoffset")
+            uoffset_ = value.GetVector4();
+
+        else if (name == "voffset")
+            voffset_ = value.GetVector4();
+
+        parameterElem = parameterElem.GetNext("parameter");
+    }
+
+
+    return true;
+}
+
+
+}

+ 92 - 0
Source/AtomicGlow/Kernel/BakeMaterial.h

@@ -0,0 +1,92 @@
+// Copyright (c) 2014-2017, 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/Resource/Image.h>
+#include <Atomic/Graphics/Material.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+class BakeMaterial : public Object
+{
+    ATOMIC_OBJECT(BakeMaterial, Object)
+
+    public:
+
+    BakeMaterial(Context* context);
+    virtual ~BakeMaterial();
+
+    bool LoadMaterial(Material* material);
+
+    Material* GetMaterial() { return material_; }
+
+    Image* GetDiffuseTexture() const { return diffuseTexture_; }
+
+    bool GetOcclusionMasked() const { return occlusionMasked_; }
+
+    const Vector4& GetUOffset() const { return uoffset_; }
+    const Vector4& GetVOffset() const { return voffset_; }
+
+private:
+
+    Variant ParseShaderParameterValue(const String& value);
+
+    SharedPtr<Material> material_;
+
+    // parameters which are loaded out of material xml
+    // as we don't load material in headless graphics mode
+
+    SharedPtr<Image> diffuseTexture_;
+
+    bool occlusionMasked_;
+
+    Vector4 uoffset_;
+    Vector4 voffset_;
+
+};
+
+class BakeMaterialCache : public Object
+{
+    friend class BakeMaterial;
+
+    ATOMIC_OBJECT(BakeMaterialCache, Object)
+
+    public:
+
+    BakeMaterialCache(Context* context);
+    virtual ~BakeMaterialCache();
+
+    BakeMaterial* GetBakeMaterial(Material* material);
+
+private:
+
+    /// Material->GetName -> BakeMaterial
+    HashMap<StringHash, SharedPtr<BakeMaterial>> bakeCache_;
+
+};
+
+
+
+}

+ 745 - 0
Source/AtomicGlow/Kernel/BakeMesh.cpp

@@ -0,0 +1,745 @@
+// Copyright (c) 2014-2017, 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 "EmbreeScene.h"
+
+#include <Atomic/Core/WorkQueue.h>
+#include <Atomic/IO/Log.h>
+#include <Atomic/Graphics/Zone.h>
+
+#include <AtomicGlow/Common/GlowSettings.h>
+
+#include "Raster.h"
+#include "LightRay.h"
+#include "SceneBaker.h"
+#include "RadianceMap.h"
+#include "BakeLight.h"
+#include "BakeMesh.h"
+
+namespace AtomicGlow
+{
+
+BakeMesh::BakeMesh(Context* context, SceneBaker *sceneBaker) : BakeNode(context, sceneBaker),
+    numVertices_(0),
+    numTriangles_(0),
+    radianceHeight_(0),
+    radianceWidth_(0),
+    bounceWidth_(0),
+    bounceHeight_(0),
+    bounceGranularity_(0),
+    embreeGeomID_(RTC_INVALID_GEOMETRY_ID),
+    numWorkItems_(0),
+    ambientColor_(Color::BLACK)
+{
+
+}
+
+BakeMesh::~BakeMesh()
+{
+
+}
+
+void BakeMesh::Pack(unsigned lightmapIdx, Vector4 tilingOffset)
+{
+    if (radianceMap_.NotNull())
+    {
+        radianceMap_->packed_ = true;
+    }
+
+    // modify the in memory scene, which will either become data for the editor
+    // or in standalone mode, saved out the scene
+    if (staticModel_)
+    {
+        staticModel_->SetLightMask(0);
+        staticModel_->SetLightmapIndex(lightmapIdx);
+        staticModel_->SetLightmapTilingOffset(tilingOffset);
+    }
+
+}
+
+
+bool BakeMesh::FillLexelsCallback(void* param, int x, int y, const Vector3& barycentric,const Vector3& dx, const Vector3& dy, float coverage)
+{
+    return ((ShaderData*) param)->bakeMesh_->LightPixel((ShaderData*) param, x, y, barycentric, dx, dy, coverage);
+}
+
+bool BakeMesh::LightPixel(ShaderData* shaderData, int x, int y, const Vector3& barycentric,const Vector3& dx, const Vector3& dy, float coverage)
+{
+    if (x >= radianceWidth_ || y >= radianceHeight_)
+        return true;
+
+    meshMutex_.Acquire();
+    bool accept = radiancePassAccept_[y * radianceWidth_ + x];
+    const Vector3& rad = radiance_[y * radianceWidth_ + x];
+
+    // check whether we've already lit this pixel
+    if (accept)
+    {
+        meshMutex_.Release();
+        return true;
+    }
+
+    radiancePassAccept_[y * radianceWidth_ + x] = true;
+
+    meshMutex_.Release();
+
+    MMTriangle* tri = &triangles_[shaderData->triangleIdx_];
+    MMVertex* verts[3];
+
+    verts[0] = &vertices_[tri->indices_[0]];
+    verts[1] = &vertices_[tri->indices_[1]];
+    verts[2] = &vertices_[tri->indices_[2]];
+
+    LightRay ray;
+    LightRay::SamplePoint& sample = ray.samplePoint_;
+
+    sample.bakeMesh = this;
+
+    sample.triangle = shaderData->triangleIdx_;
+    sample.barycentric = barycentric;
+
+    sample.radianceX = x;
+    sample.radianceY = y;
+
+    sample.position = verts[0]->position_ * barycentric.x_ +
+                      verts[1]->position_ * barycentric.y_ +
+                      verts[2]->position_ * barycentric.z_;
+
+    sample.normal = verts[0]->normal_ * barycentric.x_ +
+                    verts[1]->normal_ * barycentric.y_ +
+                    verts[2]->normal_ * barycentric.z_;
+
+    sample.uv0  = verts[0]->uv0_ * barycentric.x_ +
+                  verts[1]->uv0_ * barycentric.y_ +
+                  verts[2]->uv0_ * barycentric.z_;
+
+    sample.uv1  = verts[0]->uv1_ * barycentric.x_ +
+                  verts[1]->uv1_ * barycentric.y_ +
+                  verts[2]->uv1_ * barycentric.z_;
+
+
+    sceneBaker_->TraceRay(&ray, bakeLights_);
+
+    return true;
+}
+
+void BakeMesh::LightTrianglesWork(const WorkItem* item, unsigned threadIndex)
+{
+    ShaderData shaderData;
+    BakeMesh* bakeMesh = shaderData.bakeMesh_ = (BakeMesh*) item->aux_;
+
+    shaderData.lightMode_ = bakeMesh->GetSceneBaker()->GetCurrentLightMode();
+    Vector2 triUV1[3];
+
+    Vector2 extents(bakeMesh->radianceWidth_, bakeMesh->radianceHeight_);
+
+    MMTriangle* start = reinterpret_cast<MMTriangle*>(item->start_);
+    MMTriangle* end = reinterpret_cast<MMTriangle*>(item->end_);
+
+    while (start <= end)
+    {
+        MMTriangle* tri = start;
+
+        shaderData.triangleIdx_ = tri - &bakeMesh->triangles_[0];
+
+        start++;
+
+        for (unsigned j = 0; j < 3; j++)
+        {
+            unsigned idx = tri->indices_[j];
+
+            triUV1[j] = bakeMesh->vertices_[idx].uv1_;
+            triUV1[j].x_ *= float(bakeMesh->radianceWidth_);
+            triUV1[j].y_ *= float(bakeMesh->radianceHeight_);
+
+            triUV1[j].x_ = Clamp<float>(triUV1[j].x_, 0.0f, bakeMesh->radianceWidth_);
+            triUV1[j].y_ = Clamp<float>(triUV1[j].y_, 0.0f, bakeMesh->radianceHeight_);
+        }
+
+        Raster::DrawTriangle(true, extents, false, triUV1, FillLexelsCallback, &shaderData );
+
+    }
+
+}
+
+
+void BakeMesh::ContributeRadiance(const LightRay* lightRay, const Vector3& radiance, GlowLightMode lightMode)
+{
+    MutexLock lock(meshMutex_);
+
+    const LightRay::SamplePoint& source = lightRay->samplePoint_;
+
+    unsigned radX = source.radianceX;
+    unsigned radY = source.radianceY;
+
+    const Vector3& v = radiance_[radY * radianceWidth_ + radX];
+
+    radianceTriIndices_[radY * radianceWidth_ + radX]  = source.triangle;
+
+    if (v.x_ < 0.0f)
+    {
+        radiance_[radY * radianceWidth_ + radX] = radiance;
+    }
+    else
+    {
+        radiance_[radY * radianceWidth_ + radX] += radiance;
+    }
+
+    if (lightMode == GLOW_LIGHTMODE_AMBIENT || !GlobalGlowSettings.giEnabled_)
+        return;
+
+    // setup GI
+
+    // TODO: filter on < radiance + bounce settings
+    // OPTIMIZE: We can exit here if last bounce pass
+
+    // this should be replaced with a GetBounceColor which uses UV0 diffuse
+    // though takes into account additional settings, materials, etc
+    Color uv0Color;
+    // TODO: need to factor in alpha baked on BakeMaterial, not UV color from diffuse texture
+    if (!GetUV0Color(source.triangle, source.barycentric, uv0Color))// || uv0Color.a_ < 0.5f)
+    {
+        return;
+    }
+
+    if (uv0Color.ToVector3().Length() < 0.15f)
+    {
+        return;
+    }
+
+    int iX = (source.radianceX) & ~(bounceGranularity_-1);
+    int iY = (source.radianceY) & ~(bounceGranularity_-1);
+
+    iX /= bounceGranularity_;
+    iY /= bounceGranularity_;
+
+    assert(iY * bounceWidth_ + iX < bounceWidth_ * bounceHeight_);
+
+    if (bounceSamples_.Null())
+    {
+        bounceSamples_ = new BounceSample[bounceWidth_ * bounceHeight_];
+
+        for (unsigned j = 0; j < bounceWidth_ * bounceHeight_; j++)
+        {
+            bounceSamples_[j].Reset();
+        }
+    }
+
+    BounceSample& b = bounceSamples_[iY * bounceWidth_ + iX];
+
+    for (unsigned i = 0 ; i < GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES; i++)
+    {
+        // check if already in use by another triangle
+        if (b.triIndex_[i] != -1 && b.triIndex_[i] != source.triangle)
+            continue;
+
+        if (b.triIndex_[i] == -1)
+        {
+            b.triIndex_[i] = source.triangle;
+            b.position_ = source.position;
+            b.radiance_ = radiance;
+            b.srcColor_ = uv0Color.ToVector3();
+            b.hits_ = 1;
+            //ATOMIC_LOGINFOF("+ Tri: %i, Pos: %s, Rad: %s, BTri: %i", b.triIndex_, b.position_.ToString().CString(), b.radiance_.ToString().CString(), i);
+            return;
+        }
+
+        //ATOMIC_LOGINFOF("- Tri: %i, Pos: %s, Rad: %s, BTri: %i", b.triIndex_, b.position_.ToString().CString(), b.radiance_.ToString().CString(), i);
+
+        b.position_ += source.position;
+        b.radiance_ += radiance;
+        b.srcColor_ += uv0Color.ToVector3();
+        b.hits_++;
+
+        break;
+    }
+}
+
+void BakeMesh::GenerateRadianceMap()
+{
+    if (radianceMap_.NotNull())
+        return;
+
+    radianceMap_ = new RadianceMap(context_, this);
+}
+
+void BakeMesh::ResetBounce()
+{
+    if (bounceSamples_.Null())
+        return;
+
+    for (unsigned i = 0; i < bounceWidth_ * bounceHeight_; i++)
+    {
+        bounceSamples_[i].Reset();
+    }
+
+}
+
+
+void BakeMesh::Light(GlowLightMode mode)
+{
+    if (mode == GLOW_LIGHTMODE_INDIRECT && !GlobalGlowSettings.giEnabled_)
+        return;
+
+    if (!GetLightmap() || !radianceWidth_ || !radianceHeight_ || !numTriangles_)
+        return;
+
+    bakeLights_.Clear();
+    sceneBaker_->QueryLights(boundingBox_, bakeLights_);
+
+    // for all triangles
+
+    for (unsigned i = 0; i < radianceWidth_ * radianceHeight_; i++)
+    {
+        radiancePassAccept_[i] = false;
+    }
+
+    WorkQueue* queue = GetSubsystem<WorkQueue>();
+
+    unsigned numTrianglePerItem = numTriangles_ / 4 ? numTriangles_ / 4 : numTriangles_;
+
+    unsigned curIdx = 0;
+    numWorkItems_ = 0;
+
+    while (true)
+    {
+        SharedPtr<WorkItem> item = queue->GetFreeItem();
+        item->priority_ = M_MAX_UNSIGNED;
+        item->workFunction_ = LightTrianglesWork;
+        item->aux_ = this;
+
+        item->start_ = &triangles_[curIdx];
+
+        unsigned endIdx = curIdx + numTrianglePerItem;
+
+        if (endIdx < numTriangles_)
+        {
+            item->end_ = &triangles_[endIdx];
+        }
+        else
+        {
+            item->end_ = &triangles_[numTriangles_ - 1];
+        }
+
+        item->sendEvent_ = true;
+
+        queue->AddWorkItem(item);
+
+        numWorkItems_++;
+
+        if (item->end_ == &triangles_[numTriangles_ - 1])
+            break;
+
+        curIdx += numTrianglePerItem + 1;
+    }
+
+
+}
+
+static unsigned CalcLightMapSize(unsigned sz)
+{
+    // highest multiple of 8, note rasterizer requires a multiple of 8!
+    sz = (sz + 8) & ~7;
+
+    if (sz > 512 && !IsPowerOfTwo(sz))
+    {
+        sz = NextPowerOfTwo(sz)/2;
+    }
+
+    sz = Clamp<unsigned>(sz, 32, GlobalGlowSettings.lightmapSize_);
+
+    return sz;
+
+}
+
+void BakeMesh::Preprocess()
+{
+
+    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
+
+    if (staticModel_ && staticModel_->GetZone())
+    {
+        ambientColor_ = staticModel_->GetZone()->GetAmbientColor();
+    }
+
+    if (staticModel_ && staticModel_->GetCastShadows())
+    {
+        // Create the embree mesh
+        embreeGeomID_ = rtcNewTriangleMesh(scene, RTC_GEOMETRY_STATIC, numTriangles_, numVertices_);
+        rtcSetUserData(scene, embreeGeomID_, this);
+        rtcSetOcclusionFilterFunction(scene, embreeGeomID_, OcclusionFilter);
+        rtcSetIntersectionFilterFunction(scene, embreeGeomID_, IntersectFilter);
+
+        // Populate vertices
+
+        float* vertices = (float*) rtcMapBuffer(scene, embreeGeomID_, RTC_VERTEX_BUFFER);
+
+        MMVertex* vIn = &vertices_[0];
+
+        for (unsigned i = 0; i < numVertices_; i++, vIn++)
+        {
+            *vertices++ = vIn->position_.x_;
+            *vertices++ = vIn->position_.y_;
+            *vertices++ = vIn->position_.z_;
+
+            // Note that RTC_VERTEX_BUFFER is 16 byte aligned, thus extra component
+            // which isn't used, though we'll initialize it
+            *vertices++ = 0.0f;
+        }
+
+        rtcUnmapBuffer(scene, embreeGeomID_, RTC_VERTEX_BUFFER);
+
+        uint32_t* triangles = (uint32_t*) rtcMapBuffer(scene, embreeGeomID_, RTC_INDEX_BUFFER);
+
+        for (size_t i = 0; i < numTriangles_; i++)
+        {
+            MMTriangle* tri = &triangles_[i];
+
+            *triangles++ = tri->indices_[0];
+            *triangles++ = tri->indices_[1];
+            *triangles++ = tri->indices_[2];
+        }
+
+        rtcUnmapBuffer(scene, embreeGeomID_, RTC_INDEX_BUFFER);
+    }
+
+    float lmScale = staticModel_->GetLightmapScale();
+
+    // if we aren't lightmapped, we just contribute to occlusion
+    if (!GetLightmap() || lmScale <= 0.0f)
+        return;
+
+    unsigned lmSize = staticModel_->GetLightmapSize();
+
+    if (!lmSize)
+    {
+        float totalarea = 0.0f;
+
+        for (unsigned i = 0; i < numTriangles_; i++)
+        {
+            MMTriangle* tri = &triangles_[i];
+
+            MMVertex* v0 = &vertices_[tri->indices_[0]];
+            MMVertex* v1 = &vertices_[tri->indices_[1]];
+            MMVertex* v2 = &vertices_[tri->indices_[2]];
+
+            totalarea += AreaOfTriangle(v0->position_,
+                                        v1->position_,
+                                        v2->position_);
+        }
+
+        totalarea = Clamp<float>(totalarea, 1, 64.0f);
+
+        lmSize = CalcLightMapSize(totalarea * 64.0f * lmScale * GlobalGlowSettings.lexelDensity_ * GlobalGlowSettings.sceneLexelDensityScale_);
+
+    }
+
+    if (lmSize < 32)
+        lmSize = 32;
+
+    if (lmSize > 2048)
+        lmSize = 2048;
+
+    radianceWidth_ = lmSize;
+    radianceHeight_ = lmSize;
+
+    // init radiance
+    radiance_ = new Vector3[radianceWidth_ * radianceHeight_];
+    radianceTriIndices_ = new int[radianceWidth_ * radianceHeight_];
+    radiancePassAccept_ = new bool[radianceWidth_ * radianceHeight_];
+
+    Vector3 v(-1, -1, -1);
+    for (unsigned i = 0; i < radianceWidth_ * radianceHeight_; i++)
+    {
+        radiance_[i] = v;
+        radianceTriIndices_[i] = -1;
+        radiancePassAccept_[i] = false;
+    }
+
+    // If GI is enabled, setup bounce metrics
+    if (GlobalGlowSettings.giEnabled_)
+    {
+
+        bounceGranularity_ = GlobalGlowSettings.giGranularity_;
+        bounceWidth_ = (radianceWidth_ + bounceGranularity_) & ~(bounceGranularity_-1);
+        bounceHeight_ = (radianceHeight_ + bounceGranularity_) & ~(bounceGranularity_-1);
+        bounceWidth_ /= bounceGranularity_;
+        bounceHeight_ /= bounceGranularity_;
+    }
+}
+
+
+bool BakeMesh::SetStaticModel(StaticModel* staticModel)
+{
+    if (!staticModel || !staticModel->GetNode())
+        return false;
+
+    staticModel_ = staticModel;
+    node_ = staticModel_->GetNode();
+
+    bakeModel_ = GetSubsystem<BakeModelCache>()->GetBakeModel(staticModel_->GetModel());
+
+    if (bakeModel_.Null())
+    {
+        return false;
+    }
+
+    ModelPacker* modelPacker = bakeModel_->GetModelPacker();
+
+    if (modelPacker->lodLevels_.Size() < 1)
+    {
+        return false;
+    }
+
+    MPLODLevel* lodLevel = modelPacker->lodLevels_[0];
+
+    // LOD must have LM coords
+
+    if (!lodLevel->HasElement(TYPE_VECTOR2, SEM_TEXCOORD, 1))
+    {
+        return false;
+    }
+
+    // materials
+
+    if (staticModel_->GetNumGeometries() != lodLevel->mpGeometry_.Size())
+    {
+        ATOMIC_LOGERROR("BakeMesh::Preprocess() - Geometry mismatch");
+        return false;
+    }
+
+    for (unsigned i = 0; i < staticModel_->GetNumGeometries(); i++)
+    {
+        BakeMaterial* bakeMaterial = GetSubsystem<BakeMaterialCache>()->GetBakeMaterial(staticModel_->GetMaterial(i));
+        bakeMaterials_.Push(bakeMaterial);
+    }
+
+    // allocate
+
+    numVertices_ = 0;
+    unsigned totalIndices = 0;
+
+    lodLevel->GetTotalCounts(numVertices_, totalIndices);
+
+    if (!numVertices_ || ! totalIndices)
+    {
+        return false;
+    }
+
+    numTriangles_ = totalIndices/3;
+
+    vertices_ = new MMVertex[numVertices_];
+    triangles_ = new MMTriangle[numTriangles_];
+
+    MMVertex* vOut = &vertices_[0];
+    MMTriangle* tri = &triangles_[0];
+
+    unsigned vertexStart = 0;
+    unsigned indexStart = 0;
+
+    const Matrix3x4& wtransform = node_->GetWorldTransform();
+
+    for (unsigned i = 0; i < lodLevel->mpGeometry_.Size(); i++)
+    {
+        MPGeometry* mpGeo = lodLevel->mpGeometry_[i];
+
+        // Setup Vertices
+
+        MPVertex* vIn = &mpGeo->vertices_[0];
+
+        for (unsigned j = 0; j < mpGeo->vertices_.Size(); j++)
+        {
+            vOut->position_ = wtransform * vIn->position_;
+            vOut->normal_ = wtransform.Rotation() * vIn->normal_;
+            vOut->uv0_ = vIn->uv0_;
+            vOut->uv1_ = vIn->uv1_;
+
+            boundingBox_.Merge(vOut->position_);
+
+            vOut++;
+            vIn++;
+        }
+
+        // Setup Triangles
+
+        for (unsigned j = 0; j < mpGeo->numIndices_; j+=3, tri++)
+        {
+            tri->materialIndex_ = i;
+            tri->indices_[0] = mpGeo->indices_[j] + vertexStart;
+            tri->indices_[1] = mpGeo->indices_[j + 1] + vertexStart;
+            tri->indices_[2] = mpGeo->indices_[j + 2] + vertexStart;
+
+            tri->normal_ = vertices_[tri->indices_[0]].normal_;
+            tri->normal_ += vertices_[tri->indices_[1]].normal_;
+            tri->normal_ += vertices_[tri->indices_[2]].normal_;
+            tri->normal_ /= 3.0f;
+            tri->normal_.Normalize();
+
+        }
+
+        indexStart  += mpGeo->numIndices_;
+        vertexStart += mpGeo->vertices_.Size();
+    }
+
+    return true;
+
+}
+
+bool BakeMesh::GetUV0Color(int triIndex, const Vector3& barycentric, Color& colorOut) const
+{
+    colorOut = Color::BLACK;
+
+    if (triIndex < 0 || triIndex >= numTriangles_)
+        return false;
+
+    const MMTriangle* tri = &triangles_[triIndex];
+    const BakeMaterial* material = bakeMaterials_[tri->materialIndex_];
+    const Image* diffuse = material->GetDiffuseTexture();
+
+    if (!diffuse)
+    {
+        return false;
+    }
+
+    const Vector2& st0 = vertices_[tri->indices_[0]].uv0_;
+    const Vector2& st1 = vertices_[tri->indices_[1]].uv0_;
+    const Vector2& st2 = vertices_[tri->indices_[2]].uv0_;
+
+    const float u = barycentric.x_, v = barycentric.y_, w = barycentric.z_;
+
+    const Vector2 st = w*st0 + u*st1 + v*st2;
+
+    int x = diffuse->GetWidth() * st.x_;
+    int y = diffuse->GetHeight() * st.y_;
+
+    if (x < 0)
+        x = diffuse->GetWidth() + x;
+
+    if (y < 0)
+        y = diffuse->GetWidth() + y;
+
+    colorOut = diffuse->GetPixel(x, y);
+
+    return true;
+}
+
+BounceBakeLight* BakeMesh::GenerateBounceBakeLight()
+{
+    if (bounceSamples_.Null())
+        return 0;
+
+    BounceSample b;
+
+    PODVector<BounceSample> bounceSamples;
+
+    for (int y = 0; y < bounceHeight_; y++)
+    {
+        for (int x = 0; x < bounceWidth_; x++)
+        {
+            const BounceSample& bSrc = bounceSamples_[y * bounceWidth_ + x];
+
+            if (bSrc.triIndex_[0] == -1)
+                continue;
+
+            b = bSrc;
+
+            // average of positions
+            b.position_ = bSrc.position_ / bSrc.hits_;
+
+            // average of colors
+            b.srcColor_ = bSrc.srcColor_ / bSrc.hits_;
+
+            if (b.srcColor_.Length() < 0.15f)
+                continue;
+
+            b.radiance_ = bSrc.radiance_;
+
+            // TODO: proper falloff
+            b.radiance_ *= 0.4f;
+
+            if ((b.radiance_/b.hits_).Length() < 0.05)
+                continue;
+
+            bounceSamples.Push(b);
+
+            const bool generateDebugNodes = false;
+            if (generateDebugNodes)
+            {
+                node_->CreateChild(ToString("Bounce Pass:%i x:%i y:%i", sceneBaker_->GetCurrentGIBounce(), x, y))->SetWorldPosition(b.position_);
+            }
+        }
+    }
+
+    if (!bounceSamples.Size())
+    {
+        ResetBounce();
+        return 0;
+    }
+
+    BounceBakeLight* bakeLight = new BounceBakeLight(context_, sceneBaker_);
+
+    bakeLight->SetBakeMesh(this);
+    bakeLight->SetBounceSamples(bounceSamples);
+
+    ResetBounce();
+
+    return bakeLight;
+}
+
+void BakeMesh::IntersectFilter(void* ptr, RTCRay& ray)
+{
+    CommonFilter(static_cast<BakeMesh*>(ptr), ray);
+}
+
+void BakeMesh::OcclusionFilter(void* ptr, RTCRay& ray)
+{
+    CommonFilter(static_cast<BakeMesh*>(ptr), ray);
+}
+
+bool BakeMesh::CommonFilter(const BakeMesh* bakeMesh, RTCRay& ray)
+{
+    if (ray.primID >= (unsigned) bakeMesh->numTriangles_)
+        return false;
+
+    const MMTriangle* tri = &bakeMesh->triangles_[ray.primID];
+    const BakeMaterial* material = bakeMesh->bakeMaterials_[tri->materialIndex_];
+
+    if (!material)
+        return false;
+
+    if (!material->GetOcclusionMasked())
+        return false;
+
+    Color color;
+    if (bakeMesh->GetUV0Color(ray.primID, Vector3(ray.u, ray.v, 1.0f-ray.u-ray.v), color))
+    {
+        if (color.a_ < 0.5f)
+        {
+            ray.geomID = RTC_INVALID_GEOMETRY_ID;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+
+}

+ 220 - 0
Source/AtomicGlow/Kernel/BakeMesh.h

@@ -0,0 +1,220 @@
+// Copyright (c) 2014-2017, 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 "Embree.h"
+
+#include <Atomic/Core/Thread.h>
+#include <Atomic/Core/Mutex.h>
+#include <Atomic/Graphics/StaticModel.h>
+
+#include "BakeMaterial.h"
+#include "BakeModel.h"
+#include "BakeNode.h"
+#include "RadianceMap.h"
+
+namespace AtomicGlow
+{
+
+using namespace Atomic;
+
+class LightRay;
+class SceneBaker;
+class BakeLight;
+class BakeMesh;
+class BounceBakeLight;
+
+class BakeMesh : public BakeNode
+{
+    friend class BounceBakeLight;
+
+    ATOMIC_OBJECT(BakeMesh, BakeNode)
+
+    public:
+
+    struct MMVertex
+    {
+        Vector3 position_;
+        Vector3 normal_;
+        Vector2 uv0_;
+        Vector2 uv1_;
+    };
+
+    struct MMTriangle
+    {
+        // index into bakeMaterials_ vector
+        unsigned materialIndex_;
+
+        Vector3 normal_;
+
+        // index into vertices_ array
+        unsigned indices_[3];
+    };
+
+    struct MMSample
+    {
+        BakeMesh* bakeMesh;
+        unsigned triangle;
+
+        // coords in radiance_
+        unsigned radianceX;
+        unsigned radianceY;
+
+        Vector3 position;
+        Vector3 normal;
+        Vector2 uv0;
+        Vector2 uv1;
+    };
+
+
+    BakeMesh(Context* context, SceneBaker* sceneBaker);
+    virtual ~BakeMesh();
+
+    bool SetStaticModel(StaticModel* staticModel);
+
+    /// Get world space bounding box
+    const BoundingBox& GetBoundingBox() const { return boundingBox_; }    
+    const SharedArrayPtr<MMVertex>& GetVertices(unsigned& numVertices) const { numVertices = numVertices_; return vertices_; }
+    const SharedArrayPtr<MMTriangle>& GetTriangles(unsigned numTriangles) const { numTriangles = numTriangles_; return triangles_; }
+
+    void Preprocess();
+
+    void Light(GlowLightMode mode);
+
+    bool GetLightmap() const { return staticModel_.Null() ? false : staticModel_->GetLightmap(); }
+
+    void ContributeRadiance(const LightRay* lightRay, const Vector3 &radiance, GlowLightMode lightMode = GLOW_LIGHTMODE_DIRECT);
+
+    int GetRadianceWidth() const { return radianceWidth_; }
+    int GetRadianceHeight() const { return radianceHeight_; }
+
+    BounceBakeLight* GenerateBounceBakeLight();
+    void GenerateRadianceMap();
+
+    inline bool GetRadiance(int x, int y, Vector3& rad, int& triIndex) const
+    {
+        rad = Vector3::ZERO;
+        triIndex = -1;
+
+        if (x < 0 || x >= radianceWidth_)
+            return false;
+
+        if (y < 0 || y >= radianceHeight_)
+            return false;
+
+        rad = radiance_[y * radianceWidth_ + x];
+
+        triIndex = radianceTriIndices_[y * radianceWidth_ + x];
+
+        if (triIndex < 0 || rad.x_ < 0.0f)
+            return false;
+
+        return true;
+    }
+
+    unsigned GetGeomID() const { return embreeGeomID_; }
+
+    inline const MMTriangle* GetTriangle(unsigned index) const
+    {
+        if (index >= numTriangles_)
+            return 0;
+
+        return &triangles_[index];
+    }
+
+    SharedPtr<RadianceMap> GetRadianceMap() const { return radianceMap_; }
+
+    StaticModel* GetStaticModel() const { return staticModel_; }
+
+    void Pack(unsigned lightmapIdx, Vector4 tilingOffset);
+
+    bool GetUV0Color(int triIndex, const Vector3 &barycentric, Color& colorOut) const;
+
+    const Color& GetAmbientColor() const { return ambientColor_; }
+
+private:
+
+    struct ShaderData
+    {
+        GlowLightMode lightMode_;
+        BakeMesh* bakeMesh_;
+        unsigned triangleIdx_;
+    };
+
+    static void LightTrianglesWork(const WorkItem* item, unsigned threadIndex);
+    static bool FillLexelsCallback(void* param, int x, int y, const Vector3& barycentric,const Vector3& dx, const Vector3& dy, float coverage);
+
+    static bool CommonFilter(const BakeMesh* bakeMesh, RTCRay& ray);
+    static void OcclusionFilter(void* ptr, RTCRay& ray);
+    static void IntersectFilter(void* ptr, RTCRay& ray);
+
+    bool LightPixel(ShaderData* shaderData, int x, int y, const Vector3& barycentric,const Vector3& dx, const Vector3& dy, float coverage);
+
+    void ResetBounce();
+
+    // mesh geometry, in world space
+
+    BoundingBox boundingBox_;
+
+    SharedArrayPtr<MMVertex> vertices_;
+    unsigned numVertices_;
+    SharedArrayPtr<MMTriangle> triangles_;
+    unsigned numTriangles_;
+
+    SharedPtr<StaticModel> staticModel_;
+
+    // resources
+    PODVector<BakeMaterial*> bakeMaterials_;
+    SharedPtr<BakeModel> bakeModel_;    
+
+    // lights affecting this mesh
+    PODVector<BakeLight*> bakeLights_;
+
+    SharedPtr<RadianceMap> radianceMap_;
+
+    // can be accessed from multiple threads
+    SharedArrayPtr<Vector3> radiance_;
+    SharedArrayPtr<bool> radiancePassAccept_;
+    // radiance -> triangle contributor
+    SharedArrayPtr<int> radianceTriIndices_;
+    SharedArrayPtr<BounceSample> bounceSamples_;
+
+    unsigned radianceHeight_;
+    unsigned radianceWidth_;
+
+    unsigned bounceWidth_;
+    unsigned bounceHeight_;
+    unsigned bounceGranularity_;
+
+    unsigned embreeGeomID_;
+
+    // multithreading
+
+    Mutex meshMutex_;
+    unsigned numWorkItems_;
+
+    Color ambientColor_;
+
+};
+
+
+}

+ 97 - 0
Source/AtomicGlow/Kernel/BakeModel.cpp

@@ -0,0 +1,97 @@
+// Copyright (c) 2014-2017, 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/IO/Log.h>
+#include <Atomic/Resource/ResourceCache.h>
+
+#include "BakeModel.h"
+
+namespace AtomicGlow
+{
+
+
+BakeModelCache::BakeModelCache(Context* context) : Object(context)
+{
+
+}
+
+BakeModelCache::~BakeModelCache()
+{
+
+}
+
+BakeModel* BakeModelCache::GetBakeModel(Model *model)
+{
+    if (!model)
+        return 0;
+
+    SharedPtr<BakeModel> bakeModel;
+
+    if (bakeCache_.TryGetValue(model->GetName(), bakeModel))
+    {
+        return bakeModel;
+    }
+
+    bakeModel = new BakeModel(context_);
+
+    if (!bakeModel->LoadModel(model))
+    {
+        return 0;
+    }
+
+    bakeCache_[model->GetName()] = bakeModel;
+
+    return bakeModel;
+
+}
+
+BakeModel::BakeModel(Context* context) : Object(context)
+{
+
+}
+
+BakeModel::~BakeModel()
+{
+
+}
+
+bool BakeModel::LoadModel(Model *model)
+{
+
+    modelPacker_ = new ModelPacker(context_);
+
+    ATOMIC_LOGINFOF("Unpacking model: %s", model->GetName().CString());
+
+    if (!modelPacker_->Unpack(model))
+        return false;
+
+    /*
+    for (unsigned i = 0; i < modelPacker_->lodLevels_.Size(); i++)
+    {
+        GenerateLODLevelAOMap(modelPacker_->lodLevels_[i]);
+    }
+    */
+
+    return true;
+}
+
+
+}

+ 68 - 0
Source/AtomicGlow/Kernel/BakeModel.h

@@ -0,0 +1,68 @@
+// Copyright (c) 2014-2017, 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 <AtomicGlow/Atlas/ModelPacker.h>
+
+namespace AtomicGlow
+{
+
+class BakeModel : public Object
+{
+    ATOMIC_OBJECT(BakeModel, Object)
+
+    public:
+
+    BakeModel(Context* context);
+    virtual ~BakeModel();
+
+    bool LoadModel(Model* model);
+
+    ModelPacker* GetModelPacker() { return modelPacker_; }
+
+private:
+
+    SharedPtr<ModelPacker> modelPacker_;
+
+};
+
+class BakeModelCache : public Object
+{
+    ATOMIC_OBJECT(BakeModelCache, Object)
+
+    public:
+
+    BakeModelCache(Context* context);
+    virtual ~BakeModelCache();
+
+    BakeModel* GetBakeModel(Model* model);
+
+private:
+
+    /// Model->GetName -> BakeModel
+    HashMap<StringHash, SharedPtr<BakeModel>> bakeCache_;
+
+};
+
+
+
+}

+ 41 - 0
Source/AtomicGlow/Kernel/BakeNode.cpp

@@ -0,0 +1,41 @@
+// Copyright (c) 2014-2017, 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 "SceneBaker.h"
+
+#include "BakeNode.h"
+
+
+namespace AtomicGlow
+{
+
+
+BakeNode::BakeNode(Context* context, SceneBaker* sceneBaker) : Object(context),
+    sceneBaker_(sceneBaker)
+{
+}
+
+BakeNode::~BakeNode()
+{
+
+}
+
+}

+ 58 - 0
Source/AtomicGlow/Kernel/BakeNode.h

@@ -0,0 +1,58 @@
+// Copyright (c) 2014-2017, 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/Scene/Node.h>
+
+#include "GlowTypes.h"
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+class SceneBaker;
+
+class BakeNode : public Object
+{
+    ATOMIC_OBJECT(BakeNode, Object)
+
+public:
+
+    BakeNode(Context* context, SceneBaker* sceneBaker);
+    virtual ~BakeNode();
+
+    SceneBaker* GetSceneBaker() { return sceneBaker_; }
+    Node* GetNode() const { return node_; }
+
+protected:
+
+    // scene graph
+    SharedPtr<Node> node_;
+
+    WeakPtr<SceneBaker> sceneBaker_;
+
+private:
+
+};
+
+}

+ 26 - 0
Source/AtomicGlow/Kernel/Embree.h

@@ -0,0 +1,26 @@
+// Copyright (c) 2014-2017, 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 <embree2/rtcore.h>
+#include <embree2/rtcore_ray.h>
+

+ 91 - 0
Source/AtomicGlow/Kernel/EmbreeScene.cpp

@@ -0,0 +1,91 @@
+// Copyright (c) 2014-2017, 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 <xmmintrin.h>
+#include <pmmintrin.h>
+#include <cmath>
+#include <cfloat>
+
+#include "Embree.h"
+
+#include <Atomic/IO/Log.h>
+
+#include "BakeMesh.h"
+#include "EmbreeScene.h"
+
+// TODO: Configurable for fast/final bakes?
+
+/*
+enum RTCSceneFlags
+{
+  // dynamic type flags
+  RTC_SCENE_STATIC     = (0 << 0),    //!< specifies static scene
+  RTC_SCENE_DYNAMIC    = (1 << 0),    //!< specifies dynamic scene
+
+  // acceleration structure flags
+  RTC_SCENE_COMPACT    = (1 << 8),    //!< use memory conservative data structures
+  RTC_SCENE_COHERENT   = (1 << 9),    //!< optimize data structures for coherent rays
+  RTC_SCENE_INCOHERENT = (1 << 10),    //!< optimize data structures for in-coherent rays (enabled by default)
+  RTC_SCENE_HIGH_QUALITY = (1 << 11),  //!< create higher quality data structures
+
+  // traversal algorithm flags
+  RTC_SCENE_ROBUST     = (1 << 16)     //!< use more robust traversal algorithms
+};
+
+*/
+
+namespace AtomicGlow
+{
+
+static void RTCErrorCallback(const RTCError code, const char* str)
+{
+    ATOMIC_LOGERRORF("RTC Error %d: %s", code, str);
+}
+
+
+EmbreeScene::EmbreeScene(Context* context) : Object(context),
+    rtcDevice_(0),
+    rtcScene_(0)
+{
+
+    // Intel says to do this, so we're doing it.
+    _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+    _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
+
+    // Create the embree device and scene.
+    rtcDevice_ = rtcNewDevice(NULL);
+
+    rtcDeviceSetErrorFunction(rtcDevice_, RTCErrorCallback);
+
+    rtcScene_ = rtcDeviceNewScene(rtcDevice_, RTC_SCENE_STATIC, RTC_INTERSECT1);
+}
+
+EmbreeScene::~EmbreeScene()
+{
+
+}
+
+void EmbreeScene::Commit()
+{
+    rtcCommit(rtcScene_);
+}
+
+}

+ 61 - 0
Source/AtomicGlow/Kernel/EmbreeScene.h

@@ -0,0 +1,61 @@
+// Copyright (c) 2014-2017, 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 "Embree.h"
+
+#include <Atomic/Core/Object.h>
+
+namespace AtomicGlow
+{
+
+using namespace Atomic;
+
+class EmbreeScenePrivate;
+class BakeMesh;
+
+class EmbreeScene : public Object
+{
+    ATOMIC_OBJECT(EmbreeScene, Object)
+
+    public:
+
+    EmbreeScene(Context* context);
+    virtual ~EmbreeScene();    
+
+    void Commit();
+
+    RTCScene GetRTCScene() const { return rtcScene_; }
+
+private:
+
+    // embree geomID -> MeshMap
+    HashMap<unsigned, BakeMesh*> meshMapLookup_;
+
+    RTCDevice rtcDevice_;
+    RTCScene rtcScene_;
+
+
+};
+
+
+}

+ 69 - 0
Source/AtomicGlow/Kernel/GlowTypes.h

@@ -0,0 +1,69 @@
+// Copyright (c) 2014-2017, 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/Math/Vector3.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+const unsigned GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES = 8;
+
+enum GlowLightMode
+{
+    GLOW_LIGHTMODE_UNDEFINED,
+    GLOW_LIGHTMODE_AMBIENT,
+    GLOW_LIGHTMODE_DIRECT,
+    GLOW_LIGHTMODE_INDIRECT,
+    GLOW_LIGHTMODE_COMPLETE
+};
+
+struct BounceSample
+{
+    int triIndex_[GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES];
+    Vector3 position_;
+    Vector3 radiance_;
+    Vector3 srcColor_;
+    int hits_;
+
+    BounceSample()
+    {
+        Reset();
+    }
+
+    void Reset()
+    {
+        for (int i = 0; i < GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES; i++)
+        {
+            triIndex_[i] = -1;
+        }
+
+        Vector3 v(-1, -1, -1);
+        position_ = radiance_ = v;
+        srcColor_ = Vector3::ZERO;
+        hits_ = 0;
+    }
+};
+
+}

+ 42 - 0
Source/AtomicGlow/Kernel/LightMap.cpp

@@ -0,0 +1,42 @@
+// Copyright (c) 2014-2017, 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/Resource/Image.h>
+
+#include "LightMap.h"
+
+namespace AtomicGlow
+{
+
+LightMap::LightMap(Context* context, int width, int height) : Object(context),
+  id_(0)
+{
+    image_ = new Image(context_);
+    image_->SetSize(width, height, 3);
+    image_->Clear(Color::BLACK);
+}
+
+LightMap::~LightMap()
+{
+
+}
+
+}

+ 64 - 0
Source/AtomicGlow/Kernel/LightMap.h

@@ -0,0 +1,64 @@
+// Copyright (c) 2014-2017, 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/Object.h>
+
+#include <AtomicGlow/Common/GlowSettings.h>
+
+namespace Atomic
+{
+
+class Image;
+
+}
+
+namespace AtomicGlow
+{
+
+using namespace Atomic;
+
+class LightMap : public Object
+{
+    ATOMIC_OBJECT(LightMap, Object)
+
+    public:
+
+    LightMap(Context* context, int width = GlobalGlowSettings.lightmapSize_, int height = GlobalGlowSettings.lightmapSize_);
+    virtual ~LightMap();
+
+    unsigned GetID() const { return id_; }
+    void SetID(unsigned id) { id_ = id; }
+
+    Image* GetImage() const { return image_; }
+    void SetImage(Image* image) { image_ = image; }
+
+private:
+
+    unsigned id_;
+
+    SharedPtr<Image> image_;
+
+};
+
+
+}

+ 337 - 0
Source/AtomicGlow/Kernel/LightMapPacker.cpp

@@ -0,0 +1,337 @@
+// Copyright (c) 2014-2017, 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 <ThirdParty/STB/stb_rect_pack.h>
+
+#include <Atomic/IO/Log.h>
+#include <Atomic/Resource/Image.h>
+
+#include "BakeMesh.h"
+#include "LightMapPacker.h"
+
+namespace AtomicGlow
+{
+
+LightMapPacker::LightMapPacker(Context* context) : Object(context)
+{
+
+}
+
+LightMapPacker::~LightMapPacker()
+{
+
+}
+
+void LightMapPacker::AddRadianceMap(RadianceMap* radianceMap)
+{
+    if (!radianceMap)
+        return;
+
+    radMaps_.Push(SharedPtr<RadianceMap>(radianceMap));
+
+}
+
+
+bool LightMapPacker::TryAddRadianceMap(RadianceMap* radMap)
+{
+
+    if (radMap->packed_)
+        return false;
+
+    const int numnodes = GlobalGlowSettings.lightmapSize_;
+
+    SharedArrayPtr<unsigned char> nodes(new unsigned char[sizeof(stbrp_node) * numnodes]);
+    SharedArrayPtr<unsigned char> rects(new unsigned char[sizeof(stbrp_rect) * (workingSet_.Size() + 1)]);
+
+    stbrp_context rectContext;
+
+    stbrp_init_target (&rectContext, numnodes, numnodes, (stbrp_node *) nodes.Get(), numnodes);
+    stbrp_rect* rect = (stbrp_rect*) rects.Get();
+
+    // add working set, we do this brute force for best results
+    for (unsigned i = 0; i < workingSet_.Size(); i++)
+    {
+        RadianceMap* wmap = workingSet_[i];
+
+        rect->id = (int) i;
+        rect->w = wmap->GetWidth() + LIGHTMAP_PADDING * 2;
+        rect->h = wmap->GetHeight() + LIGHTMAP_PADDING * 2;
+
+        rect++;
+    }
+
+    rect->id = (int) workingSet_.Size();
+    rect->w = radMap->GetWidth() + LIGHTMAP_PADDING * 2;
+    rect->h = radMap->GetHeight() + LIGHTMAP_PADDING * 2;
+
+
+    if (!stbrp_pack_rects (&rectContext, (stbrp_rect *)rects.Get(), workingSet_.Size() + 1))
+    {
+        return false;
+    }
+
+    return true;
+
+}
+
+void LightMapPacker::DilatedBlit(const Image* srcImage, Image* destImage, const IntRect& destRect)
+{
+    for (int y = 0; y < srcImage->GetHeight(); y++)
+    {
+        for (int x = 0; x < srcImage->GetWidth(); x++)
+        {
+            destImage->SetPixelInt(destRect.left_ + x + LIGHTMAP_PADDING,
+                                   destRect.top_ + y + LIGHTMAP_PADDING,
+                                   srcImage->GetPixelInt(x, y));
+        }
+    }
+
+    // dilate top and bottom
+    for (int x = 0; x < srcImage->GetWidth(); x++)
+    {
+        for (int i = 0; i < LIGHTMAP_PADDING; i++)
+        {
+            destImage->SetPixelInt(destRect.left_ + x + LIGHTMAP_PADDING,
+                                   destRect.top_ + i,
+                                   srcImage->GetPixelInt(x, 0));
+
+            destImage->SetPixelInt(destRect.left_ + x + LIGHTMAP_PADDING,
+                                   destRect.bottom_ - i,
+                                   srcImage->GetPixelInt(x, srcImage->GetHeight() - 1));
+        }
+
+    }
+
+    // dilate left and right
+    for (int y = 0; y < srcImage->GetHeight(); y++)
+    {
+        for (int i = 0; i < LIGHTMAP_PADDING; i++)
+        {
+            destImage->SetPixelInt(destRect.left_ + i,
+                                   destRect.top_ + y + LIGHTMAP_PADDING,
+                                   srcImage->GetPixelInt(0, y));
+
+            destImage->SetPixelInt(destRect.right_ - i,
+                                   destRect.top_ + y + LIGHTMAP_PADDING,
+                                   srcImage->GetPixelInt(srcImage->GetWidth() - 1, y));
+        }
+
+    }
+
+    // dilate corners
+    for (int y = LIGHTMAP_PADDING - 1; y >= 0 ; y--)
+    {
+        for (int x = LIGHTMAP_PADDING - 1; x >= 0 ; x--)
+        {
+
+            // upper left
+            unsigned pixel = destImage->GetPixelInt(destRect.left_ + x + 1, destRect.top_ + y + 1);
+            //pixel = Color::BLUE.ToUInt();
+            destImage->SetPixelInt(destRect.left_ + x, destRect.top_ + y, pixel);
+
+            // upper right
+            pixel = destImage->GetPixelInt(destRect.right_ - x - 1, destRect.top_ + y + 1);
+            //pixel = Color::RED.ToUInt();
+            destImage->SetPixelInt(destRect.right_ - x, destRect.top_ + y, pixel);
+
+            // lower left
+            pixel = destImage->GetPixelInt(destRect.left_ + x + 1, destRect.bottom_ - y - 1);
+            //pixel = Color::GREEN.ToUInt();
+            destImage->SetPixelInt(destRect.left_ + x, destRect.bottom_ - y, pixel);
+
+            // lower right
+            pixel = destImage->GetPixelInt(destRect.right_ - x - 1, destRect.bottom_ - y - 1);
+            //pixel = Color::MAGENTA.ToUInt();
+            destImage->SetPixelInt(destRect.right_ - x, destRect.bottom_ - y, pixel);
+        }
+    }
+
+}
+
+void LightMapPacker::EmitLightmap(unsigned lightMapID)
+{
+    int width = GlobalGlowSettings.lightmapSize_;
+    int height = GlobalGlowSettings.lightmapSize_;
+
+    // see note in stbrp_init_target docs
+    int numnodes = width;
+
+    SharedArrayPtr<unsigned char> nodes(new unsigned char[sizeof(stbrp_node) * numnodes]);
+    SharedArrayPtr<unsigned char> rects(new unsigned char[sizeof(stbrp_rect) * workingSet_.Size()]);
+
+    stbrp_context rectContext;
+
+    stbrp_init_target (&rectContext, width, height, (stbrp_node *) nodes.Get(), numnodes);
+    stbrp_rect* rect = (stbrp_rect*) rects.Get();
+
+    for (unsigned i = 0; i < workingSet_.Size(); i++)
+    {
+        RadianceMap* radMap = workingSet_[i];
+
+        rect->id = (int) i;
+        rect->w = radMap->GetWidth() + LIGHTMAP_PADDING * 2;
+        rect->h = radMap->GetHeight() + LIGHTMAP_PADDING * 2;
+
+        rect++;
+    }
+
+    if (!stbrp_pack_rects (&rectContext, (stbrp_rect *)rects.Get(), workingSet_.Size()))
+    {
+        ATOMIC_LOGERROR("SceneBaker::Light() - not all rects packed");
+        return;
+    }
+
+    SharedPtr<Image> image(new Image(context_));
+    image->SetSize(width, height, 3);
+    image->Clear(Color::CYAN);
+
+    rect = (stbrp_rect*) rects.Get();
+
+    for (unsigned i = 0; i < workingSet_.Size(); i++)
+    {
+        RadianceMap* radMap = workingSet_[i];
+
+        if (!rect->was_packed)
+        {
+            ATOMIC_LOGERROR("LightMapPacker::Light() - skipping unpacked lightmap");
+            continue;
+        }
+
+        DilatedBlit(radMap->image_, image, IntRect(rect->x, rect->y, rect->x + rect->w - 1, rect->y + rect->h - 1));
+
+        radMap->bakeMesh_->Pack(lightMapID, Vector4(float(radMap->image_->GetWidth())/float(width),
+                                                    float(radMap->image_->GetHeight())/float(height),
+                                                    float(rect->x + LIGHTMAP_PADDING)/float(width),
+                                                    float(rect->y + LIGHTMAP_PADDING)/float(height)));
+
+        rect++;
+    }
+
+    // dilate left and right maximum extents
+    for (int i = 0; i < height; i++)
+    {
+        image->SetPixelInt(width -1, i, image->GetPixelInt(0, i));
+    }
+
+    for (int i = 0; i < width; i++)
+    {
+        image->SetPixelInt(i, height - 1, image->GetPixelInt(i, 0));
+    }
+
+    SharedPtr<LightMap> lightmap(new LightMap(context_));
+    lightMaps_.Push(lightmap);
+
+    lightmap->SetID(lightMapID);
+    lightmap->SetImage(image);
+
+    workingSet_.Clear();
+
+}
+
+void LightMapPacker::SaveLightmaps()
+{
+    for (unsigned i = 0; i < lightMaps_.Size(); i++)
+    {
+        LightMap* lightmap = lightMaps_[i];
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+        String filename = ToString("C:/Dev/atomic/AtomicExamplesPrivate/AtomicGlowTests/TestScene1/Resources/Textures/Scene_Lightmap%u.png", lightmap->GetID());
+#else
+        const char* format = GlobalGlowSettings.outputFormat_ == GLOW_OUTPUT_PNG ? "png" : "dds";
+        String filename = ToString("%s/Resources/Textures/Scene_Lightmap%u.%s", GlobalGlowSettings.projectPath_.CString(), lightmap->GetID(), format);
+#endif
+
+        //ATOMIC_LOGINFOF("Saving Lightmap: %s", filename.CString());
+
+        GlobalGlowSettings.outputFormat_ == GLOW_OUTPUT_PNG ? lightmap->GetImage()->SavePNG(filename) : lightmap->GetImage()->SaveDDS(filename);
+
+    }
+
+}
+
+static bool CompareRadianceMap(RadianceMap* lhs, RadianceMap* rhs)
+{
+    int lhsWeight = lhs->GetWidth();
+    lhsWeight += lhs->GetHeight();
+
+    int rhsWeight = rhs->GetWidth();
+    rhsWeight += rhs->GetHeight();
+
+    // sort from biggest to smallest
+    return lhsWeight > rhsWeight;
+}
+
+
+void LightMapPacker::Pack()
+{
+    unsigned lightmapID = 0;
+
+    SharedPtr<LightMap> lightmap;
+
+    Sort(radMaps_.Begin(), radMaps_.End(), CompareRadianceMap);
+
+    for (unsigned i = 0; i < radMaps_.Size(); i++)
+    {
+        RadianceMap* radMap = radMaps_[i];
+
+        if (radMap->packed_)
+            continue;
+
+        if (radMap->GetWidth() >= GlobalGlowSettings.lightmapSize_ || radMap->GetHeight() >= GlobalGlowSettings.lightmapSize_)
+        {
+            lightmap = new LightMap(context_);
+            lightMaps_.Push(lightmap);
+
+            lightmap->SetID(lightmapID);
+            lightmap->SetImage(radMap->image_);
+
+            radMap->bakeMesh_->Pack(lightmapID, Vector4(1.0f, 1.0f, 0.0f, 0.0f));
+
+            lightmapID++;
+
+            continue;
+        }
+
+        workingSet_.Push(radMap);
+
+        for (unsigned j = 0; j < radMaps_.Size(); j++)
+        {
+            if (i == j)
+                continue;
+
+            RadianceMap* otherMap = radMaps_[j];
+
+            if (TryAddRadianceMap(otherMap))
+            {
+                workingSet_.Push(otherMap);
+            }
+
+        }
+
+        EmitLightmap(lightmapID++);
+        workingSet_.Clear();
+
+    }
+
+}
+
+
+}

+ 75 - 0
Source/AtomicGlow/Kernel/LightMapPacker.h

@@ -0,0 +1,75 @@
+// Copyright (c) 2014-2017, 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/Object.h>
+
+#include "BakeMesh.h"
+#include "LightMap.h"
+
+namespace Atomic
+{
+
+class Image;
+
+}
+
+namespace AtomicGlow
+{
+
+using namespace Atomic;
+
+// on 4 pixel boundry to prevent bilinear bleeds and for DDS 4x4 compression
+static const int LIGHTMAP_PADDING = 4;
+
+class LightMapPacker : public Object
+{
+    ATOMIC_OBJECT(LightMapPacker, Object)
+
+    public:
+
+    LightMapPacker(Context* context);
+    virtual ~LightMapPacker();
+
+    void AddRadianceMap(RadianceMap* radianceMap);
+
+    void Pack();
+
+    void SaveLightmaps();
+
+private:
+
+    /// Attempts to add a radiance map to current working set
+    bool TryAddRadianceMap(RadianceMap* radMap);
+    void DilatedBlit(const Image* srcImage, Image* destImage, const IntRect& destRect);
+    void EmitLightmap(unsigned lightMapID);
+
+    Vector<SharedPtr<RadianceMap>> radMaps_;
+    Vector<SharedPtr<LightMap>> lightMaps_;
+
+
+    PODVector<RadianceMap*> workingSet_;
+
+};
+
+
+}

+ 73 - 0
Source/AtomicGlow/Kernel/LightRay.cpp

@@ -0,0 +1,73 @@
+// Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
+// Copyright 2009-2017 Intel Corporation
+//
+// 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 <xmmintrin.h>
+#include <pmmintrin.h>
+#include <cmath>
+#include <cfloat>
+
+#include "LightRay.h"
+
+namespace AtomicGlow
+{
+
+LightRay::LightRay()
+{
+
+}
+
+LightRay::~LightRay()
+{
+
+}
+
+void LightRay::ClearHit()
+{
+    rtcRay_.geomID = RTC_INVALID_GEOMETRY_ID;
+    rtcRay_.primID = RTC_INVALID_GEOMETRY_ID;
+    rtcRay_.instID = RTC_INVALID_GEOMETRY_ID;
+    rtcRay_.u = 0.0f;
+    rtcRay_.v = 0.0f;
+    rtcRay_.Ng[0] = rtcRay_.Ng[1] = rtcRay_.Ng[2] = 0.0f;
+}
+
+
+void LightRay::SetupRay(const Vector3& origin, const Vector3& dir, float tNear, float tFar)
+{
+    rtcRay_.org[0] = origin.x_;
+    rtcRay_.org[1] = origin.y_;
+    rtcRay_.org[2] = origin.z_;
+
+    rtcRay_.dir[0] = dir.x_;
+    rtcRay_.dir[1] = dir.y_;
+    rtcRay_.dir[2] = dir.z_;
+
+    rtcRay_.tnear = tNear;
+    rtcRay_.tfar = tFar;
+
+    rtcRay_.mask = 0xFFFFFFFF;
+    rtcRay_.time = 0.0f;
+
+    ClearHit();
+}
+
+}

+ 85 - 0
Source/AtomicGlow/Kernel/LightRay.h

@@ -0,0 +1,85 @@
+
+// Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
+// Copyright 2009-2017 Intel Corporation
+//
+// 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 "Embree.h"
+
+#include "Atomic/Math/Vector3.h"
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+class BakeMesh;
+
+class LightRay
+{
+
+public:
+
+    LightRay();
+    ~LightRay();
+
+    struct SamplePoint
+    {
+        SamplePoint()
+        {
+            Clear();
+        }
+
+        void Clear()
+        {
+            bakeMesh = 0;
+            triangle = 0;
+            radianceX = 0;
+            radianceY = 0;
+            position = normal = Vector3::ZERO;
+            uv0 = uv0 = Vector2::ZERO;
+        }
+
+        BakeMesh* bakeMesh;
+        unsigned triangle;
+
+        // coords in radiance_
+        unsigned radianceX;
+        unsigned radianceY;
+
+        Vector3 barycentric;
+        Vector3 position;
+        Vector3 normal;
+        Vector2 uv0;
+        Vector2 uv1;
+    };
+
+    void SetupRay(const Vector3& origin, const Vector3& dir, float tNear = 0.001f, float tFar = 99999.0f);
+
+    void ClearHit();
+
+    RTCRay rtcRay_;
+    SamplePoint samplePoint_;
+
+};
+
+}

+ 339 - 0
Source/AtomicGlow/Kernel/RadianceMap.cpp

@@ -0,0 +1,339 @@
+
+#include "LightMap.h"
+#include "BakeMesh.h"
+#include "RadianceMap.h"
+
+namespace AtomicGlow
+{
+
+RadianceMap::RadianceMap(Context* context, BakeMesh* bakeMesh) : Object(context),
+    bakeMesh_(bakeMesh),    
+    packed_(false)
+{
+    int width = bakeMesh->GetRadianceWidth();
+    int height = bakeMesh->GetRadianceHeight();
+
+    image_ = new Image(context_);
+    image_->SetSize(width, height, 2, 3);
+
+    Vector3 rad;
+    int triIndex;
+    Color c;
+
+    for (unsigned y = 0; y <height; y++)
+    {
+        for (unsigned x = 0; x < width; x++)
+        {
+            if (!bakeMesh->GetRadiance(x, y, rad, triIndex))
+            {
+                image_->SetPixel(x, y, 0, Color::MAGENTA);
+                image_->SetPixel(x, y, 1, Color::BLACK);
+                continue;
+            }
+
+            if (rad.Length() > 3.0f)
+            {
+                rad.Normalize();
+                rad *= 3.0f;
+            }
+
+            c.r_ = rad.x_;
+            c.g_ = rad.y_;
+            c.b_ = rad.z_;
+
+            image_->SetPixel(x, y, c);
+            // mark as a valid pixel
+            image_->SetPixel(x, y, 1, Color::RED);
+        }
+    }
+
+    // blur before fill
+    Blur();
+
+    const int maxDist = 7;
+    int d = 1;
+    while (FillInvalidPixels(d) && d <= maxDist)
+    {
+        d++;
+    }
+
+    //Downsample();
+
+}
+
+bool RadianceMap::CheckValidPixel(int x, int y, Color &color)
+{
+
+    if (x < 0 || x >= image_->GetWidth())
+        return false;
+
+    if (y < 0 || y >= image_->GetHeight())
+        return false;
+
+    color = image_->GetPixel(x, y, 1);
+
+    if (color == Color::BLACK)
+        return false;
+
+    color = image_->GetPixel(x, y, 0);
+
+    return true;
+}
+
+bool RadianceMap::FillInvalidPixels(int searchDistance)
+{
+    bool result = false;
+
+    PODVector<Vector2> coords;
+
+    // left
+    coords.Push(Vector2(-searchDistance, 0));
+    // right
+    coords.Push(Vector2(searchDistance, 0));
+
+    // down
+    coords.Push(Vector2(0, searchDistance));
+    // up
+    coords.Push(Vector2(0, -searchDistance));
+
+    coords.Push(Vector2(-searchDistance, -searchDistance));
+    coords.Push(Vector2(searchDistance, searchDistance));
+    coords.Push(Vector2(-searchDistance, searchDistance));
+    coords.Push(Vector2(searchDistance, -searchDistance));
+
+
+    int width = image_->GetWidth();
+    int height = image_->GetHeight();
+
+    for (int x = 0; x < width; x++)
+    {
+        for (int y = 0; y < height; y++)
+        {
+            Color c;
+            HashMap<int, PODVector<Color>> colors;
+
+            if (!CheckValidPixel(x, y , c))
+            {
+                // we have an unitialized pixel, search for an initialized neighbor
+                for (unsigned i = 0; i< coords.Size(); i++)
+                {
+                    const Vector2& coord = coords[i];
+
+                    if (CheckValidPixel(x + coord.x_, y + coord.y_, c))
+                    {
+                        Vector3 rad;
+                        int triIndex;
+
+                        bakeMesh_->GetRadiance(x + coord.x_, y + coord.y_, rad, triIndex);
+
+                        // triIndex can be -1, for a previously filled pixel
+                        colors[triIndex].Push(c);
+                    }
+                }
+
+                if (colors.Size())
+                {
+                    result = true;
+
+                    HashMap<int, PODVector<Color>>::ConstIterator itr = colors.Begin();
+                    int bestTri = -2;
+                    int bestCount = 0;
+                    while (itr != colors.End())
+                    {
+                        // only consider the previous fill colors, if we don't have any
+                        // valid tri colors
+                        if (itr->first_ < 0)
+                        {
+                            itr++;
+                            continue;
+                        }
+
+                        if (itr->second_.Size() > bestCount)
+                        {
+                            bestCount = itr->second_.Size();
+                            bestTri = itr->first_;
+                        }
+
+                        itr++;
+                    }
+
+                    if (bestTri < 0)
+                    {
+                        if (!colors.Contains(-1))
+                        {
+                            // shouldn't happen
+                            continue;
+                        }
+
+                        // use the previous fill as we don't have a valid tri
+                        bestTri = -1;
+
+                    }
+
+                    const PODVector<Color>& triColors = colors[bestTri];
+
+                    c = Color::BLACK;
+                    for (unsigned i = 0; i < triColors.Size(); i++)
+                    {
+                        c += triColors[i];
+                    }
+
+                    c.r_ /= triColors.Size();
+                    c.g_ /= triColors.Size();
+                    c.b_ /= triColors.Size();
+
+                    image_->SetPixel(x, y, c);
+                    image_->SetPixel(x, y, 1, Color::RED);
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+
+void RadianceMap::Blur()
+{
+    int width = image_->GetWidth();
+    int height = image_->GetHeight();
+
+    SharedPtr<Image> target(new Image(context_));
+    target->SetSize(width, height, 2, 3);
+    target->SetData(image_->GetData());
+
+    Color color;
+    float validPixels;
+    int minK, maxK, minL, maxL;
+    for (int i = 0; i < width; ++i)
+    {
+        for (int j = 0; j < height; ++j)
+        {
+
+            Vector3 rad;
+            int destTriIndex;
+
+            if (!bakeMesh_->GetRadiance(i, j, rad, destTriIndex))
+                continue;
+
+            color = Color::BLACK;
+            validPixels = 0;
+
+            minK = i - 1;
+            if (minK < 0)
+                minK = 0;
+            maxK = i + 1;
+            if (maxK >= width)
+                maxK = width - 1;
+            minL = j - 1;
+            if (minL < 0)
+                minL = 0;
+            maxL = j + 1;
+            if (maxL >= height)
+                maxL = height - 1;
+
+            for (int k = minK; k <= maxK; ++k)
+            {
+                for (int l = minL - 1; l < maxL; ++l)
+                {
+
+                    int tindex;
+                    if (!bakeMesh_->GetRadiance(i, j, rad, tindex))
+                        continue;
+
+                    if (tindex != destTriIndex)
+                        continue;
+
+                    Color c;
+                    if (!CheckValidPixel(k, l, c))
+                        continue;
+
+                    color += c;
+                    ++validPixels;
+                }
+            }
+
+            if (validPixels)
+            {
+                color.r_ /= validPixels;
+                color.g_ /= validPixels;
+                color.b_ /= validPixels;
+                target->SetPixel(i, j, color);
+            }
+        }
+    }
+
+    image_->SetData(target->GetData());
+}
+
+bool RadianceMap::Downsample()
+{
+    // Simple average downsample gives nice results
+
+    SharedPtr<Image> tmp(new Image(context_));
+    tmp->SetSize(image_->GetWidth()/2, image_->GetHeight()/2, 3);
+    tmp->Clear(Color::BLACK);
+
+    for (int y = 0; y < tmp->GetHeight(); y++)
+    {
+        for (int x = 0; x < tmp->GetWidth(); x++)
+        {
+
+            int validColors = 0;
+
+            Color c = Color::BLACK;
+
+            Color tc = image_->GetPixel(x * 2, y * 2);
+            if (tc != Color::BLACK)
+            {
+                c += tc;
+                validColors++;
+            }
+
+            tc = image_->GetPixel(x * 2 + 1, y * 2);
+            if (tc != Color::BLACK)
+            {
+                c += tc;
+                validColors++;
+            }
+
+            tc = image_->GetPixel(x * 2, y * 2 + 1);
+            if (tc != Color::BLACK)
+            {
+                c += tc;
+                validColors++;
+            }
+
+            tc = image_->GetPixel(x * 2 + 1, y * 2 + 1);
+            if (tc != Color::BLACK)
+            {
+                c += tc;
+                validColors++;
+            }
+
+            if (!validColors)
+                continue;
+
+            c.r_ /= validColors;
+            c.g_ /= validColors;
+            c.b_ /= validColors;
+            c.a_ = 1.0f;
+            tmp->SetPixel(x, y, c);
+        }
+
+    }
+
+    image_->SetSize(tmp->GetWidth(), tmp->GetHeight(), 3);
+    image_->SetData(tmp->GetData());
+
+    return true;
+
+
+}
+
+RadianceMap::~RadianceMap()
+{
+
+}
+
+}

+ 60 - 0
Source/AtomicGlow/Kernel/RadianceMap.h

@@ -0,0 +1,60 @@
+// Copyright (c) 2014-2017, 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/Resource/Image.h>
+
+
+namespace AtomicGlow
+{
+
+class BakeMesh;
+
+using namespace Atomic;
+
+class RadianceMap : public Object
+{
+    ATOMIC_OBJECT(RadianceMap, Object)
+
+    public:
+
+    RadianceMap(Context* context, BakeMesh* bakeMesh);
+    virtual ~RadianceMap();
+
+    int GetWidth() const { return image_.Null() ? 0 : image_->GetWidth(); }
+    int GetHeight() const { return image_.Null() ? 0 : image_->GetHeight(); }
+
+    SharedPtr<BakeMesh> bakeMesh_;
+    SharedPtr<Image> image_;
+    bool packed_;
+
+private:
+
+    bool CheckValidPixel(int x, int y, Color& color);
+    bool FillInvalidPixels(int searchDistance = 1);
+    void Blur();
+    bool Downsample();
+
+};
+
+
+}

+ 708 - 0
Source/AtomicGlow/Kernel/Raster.cpp

@@ -0,0 +1,708 @@
+
+#include "Raster.h"
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+#include <float.h>
+#endif
+
+namespace AtomicGlow
+{
+
+namespace
+{
+
+/// Return the maximum of the two arguments. For floating point values, it returns the second value if the first is NaN.
+template <typename T>
+inline const T & _max(const T & a, const T & b)
+{
+    return (b < a) ? a : b;
+}
+
+/// Return the maximum of the three arguments.
+template <typename T>
+inline const T & _max3(const T & a, const T & b, const T & c)
+{
+    return _max(a, _max(b, c));
+}
+
+/// Return the minimum of two values.
+template <typename T>
+inline const T & _min(const T & a, const T & b)
+{
+    return (a < b) ? a : b;
+}
+
+/// Return the maximum of the three arguments.
+template <typename T>
+inline const T & _min3(const T & a, const T & b, const T & c)
+{
+    return _min(a, _min(b, c));
+}
+
+/// Clamp between two values.
+template <typename T>
+inline const T & _clamp(const T & x, const T & a, const T & b)
+{
+    return _min(_max(x, a), b);
+}
+
+inline bool isFinite(const float f)
+{
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    return _finite(f) != 0;
+#endif
+
+#ifdef ATOMIC_PLATFORM_OSX
+    return isfinite(f);
+#endif
+
+#ifdef ATOMIC_PLATFORM_LINUX
+    return finitef(f);
+#endif
+
+}
+
+static inline float delta(float bot, float top, float ih)
+{
+    return (bot - top) * ih;
+}
+
+static inline Vector2 delta(const Vector2& bot, const Vector2& top, float ih)
+{
+    return (bot - top) * ih;
+}
+
+static inline Vector3 delta(const Vector3& bot, const Vector3& top, float ih)
+{
+    return (bot - top) * ih;
+}
+
+// @@ The implementation in nvmath.h should be equivalent.
+static inline int iround(float f)
+{
+    // @@ Optimize this.
+    return int(floorf(f+0.5f));
+    //return int(round(f));
+    //return int(f);
+}
+
+
+class ClippedTriangle
+{
+public:
+    ClippedTriangle(const Vector2& a, const Vector2& b, const Vector2& c)
+    {
+        m_numVertices = 3;
+        m_activeVertexBuffer = 0;
+
+        m_verticesA[0]=a;
+        m_verticesA[1]=b;
+        m_verticesA[2]=c;
+
+        m_vertexBuffers[0] = m_verticesA;
+        m_vertexBuffers[1] = m_verticesB;
+    }
+
+    unsigned vertexCount()
+    {
+        return m_numVertices;
+    }
+
+    const Vector2 * vertices()
+    {
+        return m_vertexBuffers[m_activeVertexBuffer];
+    }
+
+    inline void clipHorizontalPlane(float offset, float clipdirection)
+    {
+        Vector2 * v  = m_vertexBuffers[m_activeVertexBuffer];
+        m_activeVertexBuffer ^= 1;
+        Vector2 * v2 = m_vertexBuffers[m_activeVertexBuffer];
+
+        v[m_numVertices] = v[0];
+
+        float dy2,   dy1 = offset - v[0].y_;
+        int   dy2in, dy1in = clipdirection*dy1 >= 0;
+        unsigned  p=0;
+
+        for (unsigned k=0; k<m_numVertices; k++)
+        {
+            dy2   = offset - v[k+1].y_;
+            dy2in = clipdirection*dy2 >= 0;
+
+            if (dy1in) v2[p++] = v[k];
+
+            if ( dy1in + dy2in == 1 ) // not both in/out
+            {
+                float dx = v[k+1].x_ - v[k].x_;
+                float dy = v[k+1].y_ - v[k].y_;
+                v2[p++] = Vector2(v[k].x_ + dy1*(dx/dy), offset);
+            }
+
+            dy1 = dy2; dy1in = dy2in;
+        }
+        m_numVertices = p;
+
+        //for (uint k=0; k<m_numVertices; k++) printf("(%f, %f)\n", v2[k].x, v2[k].y); printf("\n");
+    }
+
+    inline void clipVerticalPlane(float offset, float clipdirection )
+    {
+        Vector2 * v  = m_vertexBuffers[m_activeVertexBuffer];
+        m_activeVertexBuffer ^= 1;
+        Vector2 * v2 = m_vertexBuffers[m_activeVertexBuffer];
+
+        v[m_numVertices] = v[0];
+
+        float dx2,   dx1   = offset - v[0].x_;
+        int   dx2in, dx1in = clipdirection*dx1 >= 0;
+        unsigned  p=0;
+
+        for (unsigned k=0; k<m_numVertices; k++)
+        {
+            dx2 = offset - v[k+1].x_;
+            dx2in = clipdirection*dx2 >= 0;
+
+            if (dx1in) v2[p++] = v[k];
+
+            if ( dx1in + dx2in == 1 ) // not both in/out
+            {
+                float dx = v[k+1].x_ - v[k].x_;
+                float dy = v[k+1].y_ - v[k].y_;
+                v2[p++] = Vector2(offset, v[k].y_ + dx1*(dy/dx));
+            }
+
+            dx1 = dx2; dx1in = dx2in;
+        }
+        m_numVertices = p;
+
+        //for (uint k=0; k<m_numVertices; k++) printf("(%f, %f)\n", v2[k].x, v2[k].y); printf("\n");
+    }
+
+    void computeAreaCentroid()
+    {
+        Vector2 * v  = m_vertexBuffers[m_activeVertexBuffer];
+        v[m_numVertices] = v[0];
+
+        m_area = 0;
+        float centroidx=0, centroidy=0;
+        for (unsigned k=0; k<m_numVertices; k++)
+        {
+            // http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
+            float f = v[k].x_*v[k+1].y_ - v[k+1].x_*v[k].y_;
+            m_area += f;
+            centroidx += f * (v[k].x_ + v[k+1].x_);
+            centroidy += f * (v[k].y_ + v[k+1].y_);
+        }
+        m_area = 0.5f * fabs(m_area);
+        if (m_area==0) {
+            m_centroid = Vector2(0.0f, 0.0f);
+        } else {
+            m_centroid = Vector2(centroidx/(6*m_area), centroidy/(6*m_area));
+        }
+    }
+
+    void clipAABox(float x0, float y0, float x1, float y1)
+    {
+        clipVerticalPlane  ( x0, -1);
+        clipHorizontalPlane( y0, -1);
+        clipVerticalPlane  ( x1,  1);
+        clipHorizontalPlane( y1,  1);
+
+        computeAreaCentroid();
+    }
+
+    Vector2 centroid()
+    {
+        return m_centroid;
+    }
+
+    float area()
+    {
+        return m_area;
+    }
+
+private:
+    Vector2 m_verticesA[7+1];
+    Vector2 m_verticesB[7+1];
+    Vector2 * m_vertexBuffers[2];
+    unsigned    m_numVertices;
+    unsigned    m_activeVertexBuffer;
+    float   m_area;
+    Vector2 m_centroid;
+};
+
+
+/// A triangle vertex.
+struct Vertex
+{
+    Vector2 pos;	// Position.
+    Vector3 tex;	// Texcoord. (Barycentric coordinate)
+};
+
+
+/// A triangle for rasterization.
+struct Triangle
+{
+    Triangle(const Vector2& v0, const Vector2& v1, const Vector2& v2, const Vector3& t0, const Vector3& t1, const Vector3& t2);
+
+    bool computeDeltas();
+
+    bool draw(const Vector2& extents, bool enableScissors, RasterSamplingCallback cb, void *param);
+    bool drawAA(const Vector2& extents, bool enableScissors, RasterSamplingCallback cb, void *param);
+
+    void flipBackface();
+    void computeUnitInwardNormals();
+
+    // Vertices.
+    Vector2 v1, v2, v3;
+    Vector2 n1, n2, n3; // unit inward normals
+    Vector3 t1, t2, t3;
+
+    // Deltas.
+    Vector3 dx, dy;
+
+    float sign;
+    bool valid;
+};
+
+
+/// Triangle ctor.
+Triangle::Triangle(const Vector2& v0, const Vector2& v1, const Vector2& v2, const Vector3& t0, const Vector3& t1, const Vector3& t2)
+{
+    // Init vertices.
+    this->v1 = v0;
+    this->v2 = v2;
+    this->v3 = v1;
+
+    // Set barycentric coordinates.
+    this->t1 = t0;
+    this->t2 = t2;
+    this->t3 = t1;
+
+    // make sure every triangle is front facing.
+    flipBackface();
+
+    // Compute deltas.
+    valid = computeDeltas();
+
+    computeUnitInwardNormals();
+}
+
+
+/// Compute texture space deltas.
+/// This method takes two edge vectors that form a basis, determines the
+/// coordinates of the canonic vectors in that basis, and computes the
+/// texture gradient that corresponds to those vectors.
+bool Triangle::computeDeltas()
+{
+    Vector2 e0 = v3 - v1;
+    Vector2 e1 = v2 - v1;
+
+    Vector3 de0 = t3 - t1;
+    Vector3 de1 = t2 - t1;
+
+    float denom = 1.0f / (e0.y_ * e1.x_ - e1.y_ * e0.x_);
+    if (!isFinite(denom)) {
+        return false;
+    }
+
+    float lambda1 = - e1.y_ * denom;
+    float lambda2 = e0.y_ * denom;
+    float lambda3 = e1.x_ * denom;
+    float lambda4 = - e0.x_ * denom;
+
+    dx = de0 * lambda1 + de1 * lambda2;
+    dy = de0 * lambda3 + de1 * lambda4;
+
+    return true;
+}
+
+// compute unit inward normals for each edge.
+void Triangle::computeUnitInwardNormals()
+{
+    n1 = v1 - v2; n1 = Vector2(-n1.y_, n1.x_); n1 = n1 * (1.0f/sqrtf(n1.x_*n1.x_ + n1.y_*n1.y_));
+    n2 = v2 - v3; n2 = Vector2(-n2.y_, n2.x_); n2 = n2 * (1.0f/sqrtf(n2.x_*n2.x_ + n2.y_*n2.y_));
+    n3 = v3 - v1; n3 = Vector2(-n3.y_, n3.x_); n3 = n3 * (1.0f/sqrtf(n3.x_*n3.x_ + n3.y_*n3.y_));
+}
+
+void Triangle::flipBackface()
+{
+    // check if triangle is backfacing, if so, swap two vertices
+    if ( ((v3.x_-v1.x_)*(v2.y_-v1.y_) - (v3.y_-v1.y_)*(v2.x_-v1.x_)) < 0 ) {
+        Vector2 hv=v1; v1=v2; v2=hv; // swap pos
+        Vector3 ht=t1; t1=t2; t2=ht; // swap tex
+    }
+}
+
+bool Triangle::draw(const Vector2 & extents, bool enableScissors, RasterSamplingCallback cb, void * param)
+{
+    // 28.4 fixed-point coordinates
+    const int Y1 = iround(16.0f * v1.y_);
+    const int Y2 = iround(16.0f * v2.y_);
+    const int Y3 = iround(16.0f * v3.y_);
+
+    const int X1 = iround(16.0f * v1.x_);
+    const int X2 = iround(16.0f * v2.x_);
+    const int X3 = iround(16.0f * v3.x_);
+
+    // Deltas
+    const int DX12 = X1 - X2;
+    const int DX23 = X2 - X3;
+    const int DX31 = X3 - X1;
+
+    const int DY12 = Y1 - Y2;
+    const int DY23 = Y2 - Y3;
+    const int DY31 = Y3 - Y1;
+
+    // Fixed-point deltas
+    const int FDX12 = DX12 << 4;
+    const int FDX23 = DX23 << 4;
+    const int FDX31 = DX31 << 4;
+
+    const int FDY12 = DY12 << 4;
+    const int FDY23 = DY23 << 4;
+    const int FDY31 = DY31 << 4;
+
+    int minx, miny, maxx, maxy;
+    if (enableScissors) {
+        int frustumX0 =  0 << 4;
+        int frustumY0 =  0 << 4;
+        int frustumX1 =  (int)extents.x_ << 4;
+        int frustumY1 =  (int)extents.y_ << 4;
+
+        // Bounding rectangle
+        minx = (_max(_min3(X1, X2, X3), frustumX0) + 0xF) >> 4;
+        miny = (_max(_min3(Y1, Y2, Y3), frustumY0) + 0xF) >> 4;
+        maxx = (_min(_max3(X1, X2, X3), frustumX1) + 0xF) >> 4;
+        maxy = (_min(_max3(Y1, Y2, Y3), frustumY1) + 0xF) >> 4;
+    }
+    else {
+        // Bounding rectangle
+        minx = (_min3(X1, X2, X3) + 0xF) >> 4;
+        miny = (_min3(Y1, Y2, Y3) + 0xF) >> 4;
+        maxx = (_max3(X1, X2, X3) + 0xF) >> 4;
+        maxy = (_max3(Y1, Y2, Y3) + 0xF) >> 4;
+    }
+
+    // Block size, standard 8x8 (must be power of two)
+    const int q = 8;
+
+    // @@ This won't work when minx,miny are negative. This code path is not used. Leaving as is for now.
+    assert(minx >= 0);
+    assert(miny >= 0);
+
+    // Start in corner of 8x8 block
+    minx &= ~(q - 1);
+    miny &= ~(q - 1);
+
+    // Half-edge constants
+    int C1 = DY12 * X1 - DX12 * Y1;
+    int C2 = DY23 * X2 - DX23 * Y2;
+    int C3 = DY31 * X3 - DX31 * Y3;
+
+    // Correct for fill convention
+    if(DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++;
+    if(DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++;
+    if(DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++;
+
+    // Loop through blocks
+    for(int y = miny; y < maxy; y += q)
+    {
+        for(int x = minx; x < maxx; x += q)
+        {
+            // Corners of block
+            int x0 = x << 4;
+            int x1 = (x + q - 1) << 4;
+            int y0 = y << 4;
+            int y1 = (y + q - 1) << 4;
+
+            // Evaluate half-space functions
+            bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0;
+            bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0;
+            bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0;
+            bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0;
+            int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3);
+
+            bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0;
+            bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0;
+            bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0;
+            bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0;
+            int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3);
+
+            bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0;
+            bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0;
+            bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0;
+            bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0;
+            int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3);
+
+            // Skip block when outside an edge
+            if(a == 0x0 || b == 0x0 || c == 0x0) continue;
+
+            // Accept whole block when totally covered
+            if(a == 0xF && b == 0xF && c == 0xF)
+            {
+                Vector3 texRow = t1 + dy*(y0 - v1.y_) + dx*(x0 - v1.x_);
+
+                for(int iy = y; iy < y + q; iy++)
+                {
+                    Vector3 tex = texRow;
+                    for(int ix = x; ix < x + q; ix++)
+                    {
+                        Vector3 tex2 = t1 + dx * (ix - v1.x_) + dy * (iy - v1.y_);
+                        if (!cb(param, ix, iy, tex2, dx, dy, 1.0)) {
+                            // early out.
+                            return false;
+                        }
+                        tex += dx;
+                    }
+                    texRow += dy;
+                }
+            }
+            else // Partially covered block
+            {
+                int CY1 = C1 + DX12 * y0 - DY12 * x0;
+                int CY2 = C2 + DX23 * y0 - DY23 * x0;
+                int CY3 = C3 + DX31 * y0 - DY31 * x0;
+                Vector3 texRow = t1 + dy*(y0 - v1.y_) + dx*(x0 - v1.x_);
+
+                for(int iy = y; iy < y + q; iy++)
+                {
+                    int CX1 = CY1;
+                    int CX2 = CY2;
+                    int CX3 = CY3;
+                    Vector3 tex = texRow;
+
+                    for(int ix = x; ix < x + q; ix++)
+                    {
+                        if(CX1 > 0 && CX2 > 0 && CX3 > 0)
+                        {
+                            Vector3 tex2 = t1 + dx * (ix - v1.x_) + dy * (iy - v1.y_);
+                            if (!cb(param, ix, iy, tex2, dx, dy, 1.0))
+                            {
+                                // early out.
+                                return false;
+                            }
+                        }
+
+                        CX1 -= FDY12;
+                        CX2 -= FDY23;
+                        CX3 -= FDY31;
+                        tex += dx;
+                    }
+
+                    CY1 += FDX12;
+                    CY2 += FDX23;
+                    CY3 += FDX31;
+                    texRow += dy;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+
+#define PX_INSIDE    1.0f/sqrt(2.0f)
+#define PX_OUTSIDE  -1.0f/sqrt(2.0f)
+
+#define BK_SIZE 8
+#define BK_INSIDE   sqrt(BK_SIZE*BK_SIZE/2.0f)
+#define BK_OUTSIDE -sqrt(BK_SIZE*BK_SIZE/2.0f)
+
+// extents has to be multiple of BK_SIZE!!
+bool Triangle::drawAA(const Vector2& extents, bool enableScissors, RasterSamplingCallback cb, void * param)
+{
+    float minx, miny, maxx, maxy;
+    if (enableScissors) {
+        // Bounding rectangle
+        minx = floorf(_max(_min3(v1.x_, v2.x_, v3.x_), 0.0f));
+        miny = floorf(_max(_min3(v1.y_, v2.y_, v3.y_), 0.0f));
+        maxx = ceilf( _min(_max3(v1.x_, v2.x_, v3.x_), extents.x_-1.0f));
+        maxy = ceilf( _min(_max3(v1.y_, v2.y_, v3.y_), extents.y_-1.0f));
+    }
+    else {
+        // Bounding rectangle
+        minx = floorf(_min3(v1.x_, v2.x_, v3.x_));
+        miny = floorf(_min3(v1.y_, v2.y_, v3.y_));
+        maxx = ceilf( _max3(v1.x_, v2.x_, v3.x_));
+        maxy = ceilf( _max3(v1.y_, v2.y_, v3.y_));
+    }
+
+    // There's no reason to align the blocks to the viewport, instead we align them to the origin of the triangle bounds.
+    minx = floorf(minx);
+    miny = floorf(miny);
+    //minx = (float)(((int)minx) & (~((int)BK_SIZE - 1))); // align to blocksize (we don't need to worry about blocks partially out of viewport)
+    //miny = (float)(((int)miny) & (~((int)BK_SIZE - 1)));
+
+    minx += 0.5; miny +=0.5;  // sampling at texel centers!
+    maxx += 0.5; maxy +=0.5;
+
+    // Half-edge constants
+    float C1 = n1.x_ * (-v1.x_) + n1.y_ * (-v1.y_);
+    float C2 = n2.x_ * (-v2.x_) + n2.y_ * (-v2.y_);
+    float C3 = n3.x_ * (-v3.x_) + n3.y_ * (-v3.y_);
+
+    // Loop through blocks
+    for(float y0 = miny; y0 <= maxy; y0 += BK_SIZE)
+    {
+        for(float x0 = minx; x0 <= maxx; x0 += BK_SIZE)
+        {
+            // Corners of block
+            float xc = (x0 + (BK_SIZE-1)/2.0f);
+            float yc = (y0 + (BK_SIZE-1)/2.0f);
+
+            // Evaluate half-space functions
+            float aC = C1 + n1.x_ * xc + n1.y_ * yc;
+            float bC = C2 + n2.x_ * xc + n2.y_ * yc;
+            float cC = C3 + n3.x_ * xc + n3.y_ * yc;
+
+            // Skip block when outside an edge
+            if( (aC <= BK_OUTSIDE) || (bC <= BK_OUTSIDE) || (cC <= BK_OUTSIDE) ) continue;
+
+            // Accept whole block when totally covered
+            if( (aC >= BK_INSIDE) && (bC >= BK_INSIDE) && (cC >= BK_INSIDE) )
+            {
+                Vector3 texRow = t1 + dy*(y0 - v1.y_) + dx*(x0 - v1.x_);
+
+                for (float y = y0; y < y0 + BK_SIZE; y++)
+                {
+                    Vector3 tex = texRow;
+                    for(float x = x0; x < x0 + BK_SIZE; x++)
+                    {
+                        if (!cb(param, (int)x, (int)y, tex, dx, dy, 1.0f))
+                        {
+                            return false;
+                        }
+                        tex += dx;
+                    }
+                    texRow += dy;
+                }
+            }
+            else // Partially covered block
+            {
+                float CY1 = C1 + n1.x_ * x0 + n1.y_ * y0;
+                float CY2 = C2 + n2.x_ * x0 + n2.y_ * y0;
+                float CY3 = C3 + n3.x_ * x0 + n3.y_ * y0;
+                Vector3 texRow = t1 + dy*(y0 - v1.y_) + dx*(x0 - v1.x_);
+
+                for(float y = y0; y < y0 + BK_SIZE; y++)
+                {
+                    float CX1 = CY1;
+                    float CX2 = CY2;
+                    float CX3 = CY3;
+                    Vector3 tex = texRow;
+
+                    for (float x = x0; x < x0 + BK_SIZE; x++)
+                    {
+                        if (CX1 >= PX_INSIDE && CX2 >= PX_INSIDE && CX3 >= PX_INSIDE)
+                        {
+                            // pixel completely covered
+                            Vector3 tex = t1 + dx * (x - v1.x_) + dy * (y - v1.y_);
+                            if (!cb(param, (int)x, (int)y, tex, dx, dy, 1.0f))
+                            {
+                                return false;
+                            }
+                        }
+                        else if ((CX1 >= PX_OUTSIDE) && (CX2 >= PX_OUTSIDE) && (CX3 >= PX_OUTSIDE))
+                        {
+                            // triangle partially covers pixel. do clipping.
+                            ClippedTriangle ct(v1-Vector2(x,y), v2-Vector2(x,y), v3-Vector2(x,y));
+                            ct.clipAABox(-0.5, -0.5, 0.5, 0.5);
+                            Vector2 centroid = ct.centroid();
+                            float area = ct.area();
+                            if (area > 0.0f)
+                            {
+                                Vector3 texCent = tex - dx*centroid.x_ - dy*centroid.y_;
+                                //nvCheck(texCent.x_ >= -0.1f && texCent.x_ <= 1.1f); // @@ Centroid is not very exact...
+                                //nvCheck(texCent.y_ >= -0.1f && texCent.y_ <= 1.1f);
+                                //nvCheck(texCent.z >= -0.1f && texCent.z <= 1.1f);
+                                //Vector3 texCent2 = t1 + dx * (x - v1.x_) + dy * (y - v1.y_);
+                                if (!cb(param, (int)x, (int)y, texCent, dx, dy, area))
+                                {
+                                    return false;
+                                }
+                            }
+                        }
+
+                        CX1 += n1.x_;
+                        CX2 += n2.x_;
+                        CX3 += n3.x_;
+                        tex += dx;
+                    }
+
+                    CY1 += n1.y_;
+                    CY2 += n2.y_;
+                    CY3 += n3.y_;
+                    texRow += dy;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+} // namespace
+
+
+/// Process the given triangle.
+bool Raster::DrawTriangle(bool antialias, const Vector2& extents, bool enableScissors, const Vector2 v[3], RasterSamplingCallback cb, void* param)
+{
+    Triangle tri(v[0], v[1], v[2], Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1));
+
+    if (tri.valid) {
+        if (antialias) {
+            return tri.drawAA(extents, enableScissors, cb, param);
+        } else {
+            return tri.draw(extents, enableScissors, cb, param);
+        }
+    }
+
+    return true;
+}
+
+inline float triangleArea(const Vector2& v1, const Vector2& v2, const Vector2& v3)
+{
+    return 0.5f * (v3.x_ * v1.y_ + v1.x_ * v2.y_ + v2.x_ * v3.y_ - v2.x_ * v1.y_ - v3.x_ * v2.y_ - v1.x_ * v3.y_);
+}
+
+/// Process the given quad.
+bool Raster::DrawQuad(bool antialias, const Vector2& extents, bool enableScissors, const Vector2 v[4], RasterSamplingCallback cb, void * param)
+{
+    bool sign0 = triangleArea(v[0], v[1], v[2]) > 0.0f;
+    bool sign1 = triangleArea(v[0], v[2], v[3]) > 0.0f;
+
+    // Divide the quad into two non overlapping triangles.
+    if (sign0 == sign1) {
+        Triangle tri0(v[0], v[1], v[2], Vector3(0,0,0), Vector3(1,0,0), Vector3(1,1,0));
+        Triangle tri1(v[0], v[2], v[3], Vector3(0,0,0), Vector3(1,1,0), Vector3(0,1,0));
+
+        if (tri0.valid && tri1.valid) {
+            if (antialias) {
+                return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param);
+            } else {
+                return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param);
+            }
+        }
+    }
+    else
+    {
+        Triangle tri0(v[0], v[1], v[3], Vector3(0,0,0), Vector3(1,0,0), Vector3(0,1,0));
+        Triangle tri1(v[1], v[2], v[3], Vector3(1,0,0), Vector3(1,1,0), Vector3(0,1,0));
+
+        if (tri0.valid && tri1.valid) {
+            if (antialias) {
+                return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param);
+            } else {
+                return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param);
+            }
+        }
+    }
+
+    return true;
+}
+
+}

+ 28 - 0
Source/AtomicGlow/Kernel/Raster.h

@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include <Atomic/Math/Vector2.h>
+#include <Atomic/Math/Vector3.h>
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+/// A callback to sample the environment. Return false to terminate rasterization.
+typedef bool (*RasterSamplingCallback)(void* param, int x, int y, const Vector3& barycentric,const Vector3& dx, const Vector3& dy, float coverage);
+
+class Raster
+{
+public:
+
+    // Process the given triangle. Returns false if rasterization was interrupted by the callback.
+    static bool DrawTriangle(bool antialias, const Vector2& extents, bool enableScissors, const Vector2 v[3], RasterSamplingCallback cb, void* param);
+
+    // Process the given quad. Returns false if rasterization was interrupted by the callback.
+    static bool DrawQuad(bool antialias, const Vector2& extents, bool enableScissors, const Vector2 v[4], RasterSamplingCallback cb, void* param);
+
+};
+
+
+}

+ 404 - 0
Source/AtomicGlow/Kernel/SceneBaker.cpp

@@ -0,0 +1,404 @@
+// Copyright (c) 2014-2017, 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 <xmmintrin.h>
+#include <pmmintrin.h>
+#include <cmath>
+#include <cfloat>
+
+#include <ThirdParty/STB/stb_rect_pack.h>
+
+#include <Atomic/Core/WorkQueue.h>
+#include <Atomic/IO/Log.h>
+#include <Atomic/Resource/ResourceCache.h>
+#include <Atomic/Graphics/Zone.h>
+#include <Atomic/Graphics/Light.h>
+#include <Atomic/Graphics/StaticModel.h>
+
+#include "BakeModel.h"
+#include "BakeMesh.h"
+#include "BakeLight.h"
+#include "EmbreeScene.h"
+#include "LightMapPacker.h"
+#include "SceneBaker.h"
+
+namespace AtomicGlow
+{
+
+SceneBaker::SceneBaker(Context* context) : Object(context),
+    currentLightMode_(GLOW_LIGHTMODE_UNDEFINED),
+    currentGIPass_(0),
+    standaloneMode_(true)
+{
+    embreeScene_ = new EmbreeScene(context_);
+}
+
+SceneBaker::~SceneBaker()
+{
+
+}
+
+bool SceneBaker::SaveLitScene()
+{
+    if (!standaloneMode_)
+    {
+        ATOMIC_LOGERROR("SceneBaker::SaveLitScene() - only supported in standalone mode");
+        return false;
+    }
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    String scenefilename = ToString("C:/Dev/atomic/AtomicExamplesPrivate/AtomicGlowTests/TestScene1/Resources/Scenes/LitScene.scene");
+#else
+    String scenefilename = ToString("%s/Resources/Scenes/LitScene.scene", GlobalGlowSettings.projectPath_.CString());
+#endif
+
+    File saveFile(context_, scenefilename, FILE_WRITE);
+    return scene_->SaveXML(saveFile);
+}
+
+bool SceneBaker::WriteBakeData(VectorBuffer& buffer)
+{
+    buffer.Clear();
+
+    // protocol is very simple right now, can easily be expanded
+
+    buffer.WriteUInt(bakeMeshes_.Size());
+
+    for (unsigned i = 0; i < bakeMeshes_.Size(); i++)
+    {
+        BakeMesh* bakeMesh = bakeMeshes_[i];
+        Node* node = bakeMesh->GetNode();
+        StaticModel* staticModel = bakeMesh->GetStaticModel();
+
+        buffer.WriteUInt(node->GetID());
+        buffer.WriteUInt(staticModel->GetID());
+        buffer.WriteUInt(staticModel->GetLightMask());
+        buffer.WriteUInt(staticModel->GetLightmapIndex());
+        buffer.WriteVector4(staticModel->GetLightmapTilingOffset());
+    }
+
+    return true;
+}
+
+bool SceneBaker::GenerateLightmaps()
+{
+    ATOMIC_LOGINFO("Generating Lightmaps");
+
+    for (unsigned i = 0; i < bakeMeshes_.Size(); i++)
+    {
+        BakeMesh* mesh = bakeMeshes_[i];
+        mesh->GenerateRadianceMap();
+    }
+
+    SharedPtr<LightMapPacker> packer(new LightMapPacker(context_));
+
+    for (unsigned i = 0; i < bakeMeshes_.Size(); i++)
+    {
+        BakeMesh* mesh = bakeMeshes_[i];
+
+        SharedPtr<RadianceMap> radianceMap = mesh->GetRadianceMap();
+
+        if (radianceMap.NotNull())
+        {
+            packer->AddRadianceMap(radianceMap);
+        }
+
+    }
+
+    packer->Pack();
+
+    packer->SaveLightmaps();
+
+    return WriteBakeData(bakeData_);
+
+}
+
+void SceneBaker::TraceRay(LightRay* lightRay, const PODVector<BakeLight*>& bakeLights_)
+{
+    for (unsigned i = 0; i < bakeLights_.Size(); i++)
+    {
+        bakeLights_[i]->Light(lightRay);
+    }
+}
+
+bool SceneBaker::LightDirect()
+{
+    // Direct Lighting
+    currentLightMode_ = GLOW_LIGHTMODE_DIRECT;
+
+    if (!bakeMeshes_.Size())
+    {
+        ATOMIC_LOGINFO("SceneBaker::LightDirect() - No bake meshes found");
+        bakeLights_.Clear();
+        return false;
+    }
+
+    for (unsigned i = 0; i < bakeMeshes_.Size(); i++)
+    {
+        BakeMesh* mesh = bakeMeshes_[i];
+        mesh->Light(GLOW_LIGHTMODE_DIRECT);
+    }
+
+    return true;
+
+}
+
+void SceneBaker::LightDirectFinish()
+{
+    bakeLights_.Clear();
+}
+
+bool SceneBaker::LightGI()
+{
+    // Indirect Lighting
+    currentLightMode_ = GLOW_LIGHTMODE_INDIRECT;
+
+    if (!GlobalGlowSettings.giEnabled_ || currentGIPass_ >= GlobalGlowSettings.giMaxBounces_)
+    {
+        return false;
+    }
+
+    ATOMIC_LOGINFOF("GI Pass #%i of %i", currentGIPass_ + 1, GlobalGlowSettings.giMaxBounces_);
+
+    BounceBakeLight* blight;
+    unsigned totalBounceSamples = 0;
+
+    for (unsigned i = 0; i < bakeMeshes_.Size(); i++)
+    {
+        BakeMesh* mesh = bakeMeshes_[i];
+        blight = mesh->GenerateBounceBakeLight();
+        if (blight)
+        {
+            bakeLights_.Push(SharedPtr<BakeLight>(blight));
+            totalBounceSamples += blight->GetNumBounceSamples();
+        }
+    }
+
+    if (!bakeLights_.Size())
+        return false;
+
+    ATOMIC_LOGINFOF("IMPORTANT: Optimize allocation of samples! Indirect lighting with %u bounce lights and %u samples", bakeLights_.Size(), totalBounceSamples);
+
+    for (unsigned i = 0; i < bakeMeshes_.Size(); i++)
+    {
+        BakeMesh* mesh = bakeMeshes_[i];
+        mesh->Light(GLOW_LIGHTMODE_INDIRECT);
+    }
+
+    return true;
+
+}
+
+void SceneBaker::LightGIFinish()
+{
+    bakeLights_.Clear();
+
+    currentGIPass_++;
+
+    if (currentGIPass_ >= GlobalGlowSettings.giMaxBounces_)
+    {
+    }
+}
+
+
+bool SceneBaker::Light(const GlowLightMode lightMode)
+{
+    if (lightMode == GLOW_LIGHTMODE_DIRECT)
+    {
+        if (!LightDirect())
+        {
+            currentLightMode_ = GLOW_LIGHTMODE_COMPLETE;
+            ATOMIC_LOGINFO("Cycle: Direct Lighting - no work to be done");
+            return false;
+        }
+
+        ATOMIC_LOGINFO("Cycle: Direct Lighting");
+
+        return true;
+    }
+
+    if (lightMode == GLOW_LIGHTMODE_INDIRECT)
+    {
+        if (!LightGI())
+        {
+            currentLightMode_ = GLOW_LIGHTMODE_COMPLETE;
+            ATOMIC_LOGINFO("Cycle: GI - no work to be done");
+            return false;
+        }
+
+        return true;
+    }
+
+
+    return true;
+}
+
+void SceneBaker::LightFinishCycle()
+{
+    if (currentLightMode_ == GLOW_LIGHTMODE_DIRECT)
+    {
+        LightDirectFinish();
+    }
+
+    if (currentLightMode_ == GLOW_LIGHTMODE_INDIRECT)
+    {
+        LightGIFinish();
+    }
+
+}
+
+void SceneBaker::QueryLights(const BoundingBox& bbox, PODVector<BakeLight*>& lights)
+{
+    lights.Clear();
+
+    for (unsigned i = 0; i < bakeLights_.Size(); i++)
+    {
+
+        // TODO: filter on zone, range, groups
+        lights.Push(bakeLights_[i]);
+
+    }
+
+}
+
+bool SceneBaker::Preprocess()
+{
+    Vector<SharedPtr<BakeMesh>>::Iterator itr = bakeMeshes_.Begin();
+
+    while (itr != bakeMeshes_.End())
+    {
+        (*itr)->Preprocess();
+        itr++;
+    }
+
+    embreeScene_->Commit();
+
+    return true;
+}
+
+bool SceneBaker::LoadScene(const String& filename)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SharedPtr<File> file = cache->GetFile(filename);
+
+    if (!file || !file->IsOpen())
+    {
+        return false;
+    }
+
+    scene_ = new Scene(context_);
+
+    if (!scene_->LoadXML(*file))
+    {
+        scene_ = 0;
+        return false;
+    }
+
+    // IMPORTANT!, if scene updates are enabled
+    // the Octree component will add work queue items
+    // and will call WorkQueue->Complete(), see Octree::HandleRenderUpdate
+    scene_->SetUpdateEnabled(false);
+
+    // Zones
+    PODVector<Node*> zoneNodes;
+    PODVector<Zone*> zones;
+    scene_->GetChildrenWithComponent<Zone>(zoneNodes, true);
+
+    for (unsigned i = 0; i < zoneNodes.Size(); i++)
+    {        
+        Zone* zone = zoneNodes[i]->GetComponent<Zone>();
+
+        if (!zone->GetNode()->IsEnabled() || !zone->IsEnabled())
+            continue;
+
+        zones.Push(zone);;
+        SharedPtr<ZoneBakeLight> zlight(new ZoneBakeLight(context_, this));
+        zlight->SetZone(zone);
+        bakeLights_.Push(zlight);
+    }
+
+    // Lights
+    PODVector<Node*> lightNodes;
+    scene_->GetChildrenWithComponent<Atomic::Light>(lightNodes, true);
+
+    for (unsigned i = 0; i < lightNodes.Size(); i++)
+    {
+        Atomic::Light* light = lightNodes[i]->GetComponent<Atomic::Light>();
+
+        if (!light->GetNode()->IsEnabled()|| !light->IsEnabled())
+            continue;
+
+        if (light->GetLightType() == LIGHT_DIRECTIONAL)
+        {
+            SharedPtr<DirectionalBakeLight> dlight(new DirectionalBakeLight(context_, this));
+            dlight->SetLight(light);
+            bakeLights_.Push(dlight);
+        }
+        else if (light->GetLightType() == LIGHT_POINT)
+        {
+            SharedPtr<PointBakeLight> dlight(new PointBakeLight(context_, this));
+            dlight->SetLight(light);
+            bakeLights_.Push(dlight);
+        }
+
+    }
+
+    // Static Models
+    PODVector<StaticModel*> staticModels;
+    scene_->GetComponents<StaticModel>(staticModels, true);
+
+    for (unsigned i = 0; i < staticModels.Size(); i++)
+    {
+        StaticModel* staticModel = staticModels[i];
+
+        if (!staticModel->GetNode()->IsEnabled() || !staticModel->IsEnabled())
+            continue;
+
+        Vector3 center = staticModel->GetWorldBoundingBox().Center();
+        int bestPriority = M_MIN_INT;
+        Zone* newZone = 0;
+
+        for (PODVector<Zone*>::Iterator i = zones.Begin(); i != zones.End(); ++i)
+        {
+            Zone* zone = *i;
+            int priority = zone->GetPriority();
+            if (priority > bestPriority && (staticModel->GetZoneMask() & zone->GetZoneMask()) && zone->IsInside(center))
+            {
+                newZone = zone;
+                bestPriority = priority;
+            }
+        }
+
+        staticModel->SetZone(newZone, false);
+
+        if (staticModel->GetModel() && (staticModel->GetLightmap() ||staticModel->GetCastShadows()))
+        {
+            SharedPtr<BakeMesh> meshMap (new BakeMesh(context_, this));
+            meshMap->SetStaticModel(staticModel);
+            bakeMeshes_.Push(meshMap);
+        }
+
+    }
+
+    return Preprocess();
+}
+
+}

+ 108 - 0
Source/AtomicGlow/Kernel/SceneBaker.h

@@ -0,0 +1,108 @@
+// Copyright (c) 2014-2017, 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/Scene/Scene.h>
+#include "GlowTypes.h"
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+class LightRay;
+class BakeMesh;
+class BakeLight;
+class EmbreeScene;
+
+class SceneBaker : public Object
+{
+    ATOMIC_OBJECT(SceneBaker, Object)
+
+    public:
+
+    SceneBaker(Context* context);
+    virtual ~SceneBaker();    
+
+    bool Light(const GlowLightMode lightMode);
+    void LightFinishCycle();
+
+    bool LoadScene(const String& filename);
+
+    bool GenerateLightmaps();
+
+    const VectorBuffer& GetBakeData() const { return bakeData_; }
+
+    GlowLightMode GetCurrentLightMode() const { return currentLightMode_; }
+
+    void QueryLights(const BoundingBox& bbox, PODVector<BakeLight*>& lights);
+
+    void TraceRay(LightRay* lightRay, const PODVector<AtomicGlow::BakeLight *>& bakeLights_);
+
+    Scene* GetScene() const { return scene_; }
+
+    EmbreeScene* GetEmbreeScene() const { return embreeScene_; }
+
+    int GetCurrentGIBounce() const {return currentGIPass_; }
+
+    bool SaveLitScene();
+
+    bool WriteBakeData(VectorBuffer& buffer);
+
+    bool GetStandaloneMode() const { return standaloneMode_; }
+
+    void SetStandaloneMode(bool standaloneMode) { standaloneMode_ = standaloneMode; }
+
+private:
+
+    //void FilterLightmap(Image* lightmap);
+    //void EmitLightmap(int lightMapIndex);
+    //bool TryAddStaticModelBaker(StaticModelBaker *bakeModel);
+
+    bool Preprocess();
+
+    /// process direct (and ao/ambient) lighting, returns true if there is work to be done
+    bool LightDirect();
+    void LightDirectFinish();
+
+    /// process a GI cycle, returns true if there is work to be done
+    bool LightGI();
+    void LightGIFinish();
+
+    SharedPtr<Scene> scene_;
+    SharedPtr<EmbreeScene> embreeScene_;
+
+    Vector<SharedPtr<BakeMesh>> bakeMeshes_;
+
+    Vector<SharedPtr<BakeLight>> bakeLights_;
+
+    VectorBuffer bakeData_;
+
+    GlowLightMode currentLightMode_;
+
+    int currentGIPass_;
+
+    bool standaloneMode_;
+
+};
+
+}

+ 46 - 34
Source/AtomicTool/AtomicTool.cpp

@@ -62,6 +62,19 @@ void AtomicTool::Setup()
     if (arguments.Contains("-toolbootstrap"))
     if (arguments.Contains("-toolbootstrap"))
         ToolEnvironment::SetBootstrapping();
         ToolEnvironment::SetBootstrapping();
 
 
+    ToolSystem* tsystem = new ToolSystem(context_);
+    context_->RegisterSubsystem(tsystem);
+
+    ToolEnvironment* env = new ToolEnvironment(context_);
+    context_->RegisterSubsystem(env);
+
+    // Initialize the ToolEnvironment
+    if (!env->Initialize(true))
+    {
+        ErrorExit("Unable to initialize tool environment");
+        return;
+    }
+
     engineParameters_["Headless"] = true;
     engineParameters_["Headless"] = true;
     engineParameters_["LogLevel"] = LOG_INFO;
     engineParameters_["LogLevel"] = LOG_INFO;
 
 
@@ -84,8 +97,32 @@ void AtomicTool::Setup()
         }
         }
     }
     }
 
 
+    SharedPtr<CommandParser> parser(new CommandParser(context_));
+
+    command_ = parser->Parse(arguments);
+    if (command_.Null())
+    {
+        String error = "No command found";
+
+        if (parser->GetErrorMessage().Length())
+            error = parser->GetErrorMessage();
+
+        ErrorExit(error);
+        return;
+    }
+
     // no default resources, AtomicTool may be run outside of source tree
     // no default resources, AtomicTool may be run outside of source tree
     engineParameters_["ResourcePaths"] = "";
     engineParameters_["ResourcePaths"] = "";
+
+    if (command_->RequiresProjectLoad())
+    {
+
+#ifdef ATOMIC_DEV_BUILD
+        engineParameters_["ResourcePrefixPaths"] = env->GetRootSourceDir() + "/Resources/";
+        engineParameters_["ResourcePaths"] = ToString("CoreData");
+#endif
+    }
+
 }
 }
 
 
 void AtomicTool::HandleCommandFinished(StringHash eventType, VariantMap& eventData)
 void AtomicTool::HandleCommandFinished(StringHash eventType, VariantMap& eventData)
@@ -182,19 +219,6 @@ void AtomicTool::Start()
 
 
     const Vector<String>& arguments = GetArguments();
     const Vector<String>& arguments = GetArguments();
 
 
-    ToolSystem* tsystem = new ToolSystem(context_);
-    context_->RegisterSubsystem(tsystem);
-
-    ToolEnvironment* env = new ToolEnvironment(context_);
-    context_->RegisterSubsystem(env);
-
-    // Initialize the ToolEnvironment
-    if (!env->Initialize(true))
-    {
-        ErrorExit("Unable to initialize tool environment");
-        return;
-    }
-
     if (activationKey_.Length())
     if (activationKey_.Length())
     {
     {
         DoActivation();
         DoActivation();
@@ -207,29 +231,19 @@ void AtomicTool::Start()
 
 
     BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
     BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
 
 
-    SharedPtr<CommandParser> parser(new CommandParser(context_));
-
-    SharedPtr<Command> cmd(parser->Parse(arguments));
-    if (!cmd)
-    {
-        String error = "No command found";
-
-        if (parser->GetErrorMessage().Length())
-            error = parser->GetErrorMessage();
-
-        ErrorExit(error);
-        return;
-    }
-
-    if (cmd->RequiresProjectLoad())
+    if (command_->RequiresProjectLoad())
     {
     {
-        if (!cmd->LoadProject())
+        if (!command_->LoadProject())
         {
         {
-            ErrorExit(ToString("Failed to load project: %s", cmd->GetProjectPath().CString()));
+            ErrorExit(ToString("Failed to load project: %s", command_->GetProjectPath().CString()));
             return;
             return;
         }
         }
 
 
-        String projectPath = cmd->GetProjectPath();
+        String projectPath = command_->GetProjectPath();
+
+        ResourceCache* cache = GetSubsystem<ResourceCache>();
+        cache->AddResourceDir(ToString("%sResources", projectPath.CString()));
+        cache->AddResourceDir(ToString("%sCache", projectPath.CString()));
 
 
         // Set the build path
         // Set the build path
         String buildFolder = projectPath + "/" + "Build";
         String buildFolder = projectPath + "/" + "Build";
@@ -249,10 +263,8 @@ void AtomicTool::Start()
 
 
     }
     }
 
 
-    command_ = cmd;
-
     // BEGIN LICENSE MANAGEMENT
     // BEGIN LICENSE MANAGEMENT
-    if (cmd->RequiresLicenseValidation())
+    if (command_->RequiresLicenseValidation())
     {
     {
         GetSubsystem<LicenseSystem>()->Initialize();
         GetSubsystem<LicenseSystem>()->Initialize();
     }
     }

+ 4 - 0
Source/AtomicTool/CMakeLists.txt

@@ -8,6 +8,10 @@ add_executable(AtomicTool ${ATOMIC_TOOL_SOURCES})
 
 
 target_link_libraries(AtomicTool ToolCore AtomicNETScript Poco Atomic)
 target_link_libraries(AtomicTool ToolCore AtomicNETScript Poco Atomic)
 
 
+if (ATOMIC_GLOW)
+    target_link_libraries(AtomicTool AtomicGlowLib)
+endif()
+
 if (WIN32)
 if (WIN32)
     target_link_libraries(AtomicTool Iphlpapi Wldap32)
     target_link_libraries(AtomicTool Iphlpapi Wldap32)
 endif()
 endif()

+ 5 - 0
Source/CMakeLists.txt

@@ -8,6 +8,7 @@ option(ATOMIC_EDITOR "Build Editor" ON)
 option(ATOMIC_DATABASE_SQLITE "Enable SQLite database subsystem" OFF)
 option(ATOMIC_DATABASE_SQLITE "Enable SQLite database subsystem" OFF)
 option(ATOMIC_DATABASE_ODBC "Enable ODBC database subsystem" OFF)
 option(ATOMIC_DATABASE_ODBC "Enable ODBC database subsystem" OFF)
 option(ATOMIC_IK "Enable inverse kinematics subsystem" OFF)
 option(ATOMIC_IK "Enable inverse kinematics subsystem" OFF)
+option(ATOMIC_GLOW "Build Atomic Glow" ON)
 
 
 add_subdirectory(ThirdParty)
 add_subdirectory(ThirdParty)
 add_subdirectory(Atomic)
 add_subdirectory(Atomic)
@@ -28,6 +29,10 @@ if (ATOMIC_DOTNET)
 endif ()
 endif ()
 
 
 if (ATOMIC_DESKTOP)
 if (ATOMIC_DESKTOP)
+
+    if (ATOMIC_GLOW)        
+        add_subdirectory(AtomicGlow)
+    endif ()
     if (ATOMIC_DOTNET OR ATOMIC_JAVASCRIPT)
     if (ATOMIC_DOTNET OR ATOMIC_JAVASCRIPT)
         add_subdirectory(AtomicTool)
         add_subdirectory(AtomicTool)
     endif ()
     endif ()

+ 407 - 0
Source/ThirdParty/STB/stb_rect_pack.c

@@ -0,0 +1,407 @@
+#include "stb_rect_pack.h"
+
+#define STB_RECT_PACK_IMPLEMENTATION
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//     IMPLEMENTATION SECTION
+//
+
+#ifdef STB_RECT_PACK_IMPLEMENTATION
+#ifndef STBRP_SORT
+#include <stdlib.h>
+#define STBRP_SORT qsort
+#endif
+
+#ifndef STBRP_ASSERT
+#include <assert.h>
+#define STBRP_ASSERT assert
+#endif
+
+#ifdef _MSC_VER
+#define STBRP__NOTUSED(v)  (void)(v)
+#else
+#define STBRP__NOTUSED(v)  (void)sizeof(v)
+#endif
+
+enum
+{
+   STBRP__INIT_skyline = 1
+};
+
+STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
+{
+   switch (context->init_mode) {
+      case STBRP__INIT_skyline:
+         STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
+         context->heuristic = heuristic;
+         break;
+      default:
+         STBRP_ASSERT(0);
+   }
+}
+
+STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
+{
+   if (allow_out_of_mem)
+      // if it's ok to run out of memory, then don't bother aligning them;
+      // this gives better packing, but may fail due to OOM (even though
+      // the rectangles easily fit). @TODO a smarter approach would be to only
+      // quantize once we've hit OOM, then we could get rid of this parameter.
+      context->align = 1;
+   else {
+      // if it's not ok to run out of memory, then quantize the widths
+      // so that num_nodes is always enough nodes.
+      //
+      // I.e. num_nodes * align >= width
+      //                  align >= width / num_nodes
+      //                  align = ceil(width/num_nodes)
+
+      context->align = (context->width + context->num_nodes-1) / context->num_nodes;
+   }
+}
+
+STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
+{
+   int i;
+#ifndef STBRP_LARGE_RECTS
+   STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
+#endif
+
+   for (i=0; i < num_nodes-1; ++i)
+      nodes[i].next = &nodes[i+1];
+   nodes[i].next = NULL;
+   context->init_mode = STBRP__INIT_skyline;
+   context->heuristic = STBRP_HEURISTIC_Skyline_default;
+   context->free_head = &nodes[0];
+   context->active_head = &context->extra[0];
+   context->width = width;
+   context->height = height;
+   context->num_nodes = num_nodes;
+   stbrp_setup_allow_out_of_mem(context, 0);
+
+   // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
+   context->extra[0].x = 0;
+   context->extra[0].y = 0;
+   context->extra[0].next = &context->extra[1];
+   context->extra[1].x = (stbrp_coord) width;
+#ifdef STBRP_LARGE_RECTS
+   context->extra[1].y = (1<<30);
+#else
+   context->extra[1].y = 65535;
+#endif
+   context->extra[1].next = NULL;
+}
+
+// find minimum y position if it starts at x1
+static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
+{
+   stbrp_node *node = first;
+   int x1 = x0 + width;
+   int min_y, visited_width, waste_area;
+
+   STBRP__NOTUSED(c);
+
+   STBRP_ASSERT(first->x <= x0);
+
+   #if 0
+   // skip in case we're past the node
+   while (node->next->x <= x0)
+      ++node;
+   #else
+   STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
+   #endif
+
+   STBRP_ASSERT(node->x <= x0);
+
+   min_y = 0;
+   waste_area = 0;
+   visited_width = 0;
+   while (node->x < x1) {
+      if (node->y > min_y) {
+         // raise min_y higher.
+         // we've accounted for all waste up to min_y,
+         // but we'll now add more waste for everything we've visted
+         waste_area += visited_width * (node->y - min_y);
+         min_y = node->y;
+         // the first time through, visited_width might be reduced
+         if (node->x < x0)
+            visited_width += node->next->x - x0;
+         else
+            visited_width += node->next->x - node->x;
+      } else {
+         // add waste area
+         int under_width = node->next->x - node->x;
+         if (under_width + visited_width > width)
+            under_width = width - visited_width;
+         waste_area += under_width * (min_y - node->y);
+         visited_width += under_width;
+      }
+      node = node->next;
+   }
+
+   *pwaste = waste_area;
+   return min_y;
+}
+
+typedef struct
+{
+   int x,y;
+   stbrp_node **prev_link;
+} stbrp__findresult;
+
+static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
+{
+   int best_waste = (1<<30), best_x, best_y = (1 << 30);
+   stbrp__findresult fr;
+   stbrp_node **prev, *node, *tail, **best = NULL;
+
+   // align to multiple of c->align
+   width = (width + c->align - 1);
+   width -= width % c->align;
+   STBRP_ASSERT(width % c->align == 0);
+
+   node = c->active_head;
+   prev = &c->active_head;
+   while (node->x + width <= c->width) {
+      int y,waste;
+      y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
+      if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
+         // bottom left
+         if (y < best_y) {
+            best_y = y;
+            best = prev;
+         }
+      } else {
+         // best-fit
+         if (y + height <= c->height) {
+            // can only use it if it first vertically
+            if (y < best_y || (y == best_y && waste < best_waste)) {
+               best_y = y;
+               best_waste = waste;
+               best = prev;
+            }
+         }
+      }
+      prev = &node->next;
+      node = node->next;
+   }
+
+   best_x = (best == NULL) ? 0 : (*best)->x;
+
+   // if doing best-fit (BF), we also have to try aligning right edge to each node position
+   //
+   // e.g, if fitting
+   //
+   //     ____________________
+   //    |____________________|
+   //
+   //            into
+   //
+   //   |                         |
+   //   |             ____________|
+   //   |____________|
+   //
+   // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
+   //
+   // This makes BF take about 2x the time
+
+   if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
+      tail = c->active_head;
+      node = c->active_head;
+      prev = &c->active_head;
+      // find first node that's admissible
+      while (tail->x < width)
+         tail = tail->next;
+      while (tail) {
+         int xpos = tail->x - width;
+         int y,waste;
+         STBRP_ASSERT(xpos >= 0);
+         // find the left position that matches this
+         while (node->next->x <= xpos) {
+            prev = &node->next;
+            node = node->next;
+         }
+         STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
+         y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
+         if (y + height < c->height) {
+            if (y <= best_y) {
+               if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
+                  best_x = xpos;
+                  STBRP_ASSERT(y <= best_y);
+                  best_y = y;
+                  best_waste = waste;
+                  best = prev;
+               }
+            }
+         }
+         tail = tail->next;
+      }
+   }
+
+   fr.prev_link = best;
+   fr.x = best_x;
+   fr.y = best_y;
+   return fr;
+}
+
+static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
+{
+   // find best position according to heuristic
+   stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
+   stbrp_node *node, *cur;
+
+   // bail if:
+   //    1. it failed
+   //    2. the best node doesn't fit (we don't always check this)
+   //    3. we're out of memory
+   if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
+      res.prev_link = NULL;
+      return res;
+   }
+
+   // on success, create new node
+   node = context->free_head;
+   node->x = (stbrp_coord) res.x;
+   node->y = (stbrp_coord) (res.y + height);
+
+   context->free_head = node->next;
+
+   // insert the new node into the right starting point, and
+   // let 'cur' point to the remaining nodes needing to be
+   // stiched back in
+
+   cur = *res.prev_link;
+   if (cur->x < res.x) {
+      // preserve the existing one, so start testing with the next one
+      stbrp_node *next = cur->next;
+      cur->next = node;
+      cur = next;
+   } else {
+      *res.prev_link = node;
+   }
+
+   // from here, traverse cur and free the nodes, until we get to one
+   // that shouldn't be freed
+   while (cur->next && cur->next->x <= res.x + width) {
+      stbrp_node *next = cur->next;
+      // move the current node to the free list
+      cur->next = context->free_head;
+      context->free_head = cur;
+      cur = next;
+   }
+
+   // stitch the list back in
+   node->next = cur;
+
+   if (cur->x < res.x + width)
+      cur->x = (stbrp_coord) (res.x + width);
+
+#ifdef _DEBUG
+   cur = context->active_head;
+   while (cur->x < context->width) {
+      STBRP_ASSERT(cur->x < cur->next->x);
+      cur = cur->next;
+   }
+   STBRP_ASSERT(cur->next == NULL);
+
+   {
+      stbrp_node *L1 = NULL, *L2 = NULL;
+      int count=0;
+      cur = context->active_head;
+      while (cur) {
+         L1 = cur;
+         cur = cur->next;
+         ++count;
+      }
+      cur = context->free_head;
+      while (cur) {
+         L2 = cur;
+         cur = cur->next;
+         ++count;
+      }
+      STBRP_ASSERT(count == context->num_nodes+2);
+   }
+#endif
+
+   return res;
+}
+
+static int rect_height_compare(const void *a, const void *b)
+{
+   const stbrp_rect *p = (const stbrp_rect *) a;
+   const stbrp_rect *q = (const stbrp_rect *) b;
+   if (p->h > q->h)
+      return -1;
+   if (p->h < q->h)
+      return  1;
+   return (p->w > q->w) ? -1 : (p->w < q->w);
+}
+
+static int rect_width_compare(const void *a, const void *b)
+{
+   const stbrp_rect *p = (const stbrp_rect *) a;
+   const stbrp_rect *q = (const stbrp_rect *) b;
+   if (p->w > q->w)
+      return -1;
+   if (p->w < q->w)
+      return  1;
+   return (p->h > q->h) ? -1 : (p->h < q->h);
+}
+
+static int rect_original_order(const void *a, const void *b)
+{
+   const stbrp_rect *p = (const stbrp_rect *) a;
+   const stbrp_rect *q = (const stbrp_rect *) b;
+   return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
+}
+
+#ifdef STBRP_LARGE_RECTS
+#define STBRP__MAXVAL  0xffffffff
+#else
+#define STBRP__MAXVAL  0xffff
+#endif
+
+STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
+{
+   int i, all_rects_packed = 1;
+
+   // we use the 'was_packed' field internally to allow sorting/unsorting
+   for (i=0; i < num_rects; ++i) {
+      rects[i].was_packed = i;
+      #ifndef STBRP_LARGE_RECTS
+      STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff);
+      #endif
+   }
+
+   // sort according to heuristic
+   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
+
+   for (i=0; i < num_rects; ++i) {
+      if (rects[i].w == 0 || rects[i].h == 0) {
+         rects[i].x = rects[i].y = 0;  // empty rect needs no space
+      } else {
+         stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
+         if (fr.prev_link) {
+            rects[i].x = (stbrp_coord) fr.x;
+            rects[i].y = (stbrp_coord) fr.y;
+         } else {
+            rects[i].x = rects[i].y = STBRP__MAXVAL;
+         }
+      }
+   }
+
+   // unsort
+   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
+
+   // set was_packed flags and all_rects_packed status
+   for (i=0; i < num_rects; ++i) {
+      rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
+      if (!rects[i].was_packed)
+         all_rects_packed = 0;
+   }
+
+   // return the all_rects_packed status
+   return all_rects_packed;
+}
+#endif

+ 384 - 0
Source/ThirdParty/embree/CHANGELOG.md

@@ -0,0 +1,384 @@
+Version History
+---------------
+
+### New Features in Embree 2.15.0
+
+-   Added `rtcCommitJoin` mode that allows thread to join a build
+    operation. When using the internal tasking system this allows
+    Embree to solely use the threads that called `rtcCommitJoin` to
+    build the scene, while previously also normal worker threads
+    participated in the build. You should no longer use  `rtcCommit`
+    to join a build.
+-   Added `rtcDeviceSetErrorFunction2` API call, which sets an error
+    callback function which additionally gets passed a user provided
+    pointer (`rtcDeviceSetErrorFunction` is now deprecated).
+-   Added `rtcDeviceSetMemoryMonitorFunction2` API call, which sets a
+    memory monitor callback function which additionally get passed a
+    user provided pointer. (`rtcDeviceSetMemoryMonitorFunction` is now
+    deprecated).
+-   Build performance for hair geometry improved up to 2x.
+-   Added API extension to use internal Morton builder, standard SAH
+    builder, and spatial split SAH builder.
+-   Added support for BSpline hair and curves. Embree uses
+    either the Bezier or BSpline basis internally, and converts other
+    curves, which requiring more memory during rendering. For reduced
+    memory consumption set the EMBREE_NATIVE_SPLINE_BASIS to the basis
+    your application uses (which is set to BEZIER by default).
+-   Setting the number of threads through an application side
+    tbb::taskscheduler_init object is now working properly.
+-   Windows and Linux releases are build using AVX512 support.
+-   Implemented hybrid traversal for hair and line segments for
+    improved ray packet performance.
+-   AVX512 code compiles with Clang 4.0.0
+-   Fixed crash when ray packets were disabled in CMake.
+
+### New Features in Embree 2.14.0
+
+-   Added `ignore_config_files` option to init flags that allows the
+    application to ignore Embree configuration files.
+-   Face-varying interpolation is now supported for subdivision surfaces.
+-   Up to 16 user vertex buffers are supported for vertex
+    attribute interpolation.
+-   Deprecated `rtcSetBoundaryMode` function, please use the new
+    `rtcSetSubdivisionMode` function.
+-   Added `RTC_SUBDIV_PIN_BOUNDARY` mode for handling boundaries of
+    subdivision meshes.
+-   Added `RTC_SUBDIV_PIN_ALL` mode to enforce linear interpolation
+    for subdivision meshes.
+-   Optimized object generation performance for dynamic scenes.
+-   Reduced memory consumption when using lots of small dynamic objects.
+-   Fixed bug for subdivision surfaces using low tessellation rates.
+-   Hair geometry now uses a new ribbon intersector that intersects with
+    ray-facing quads. The new intersector also returns the
+    v-coordinate of the hair intersection, and fixes artefacts at junction
+    points between segments, at the cost of a small performance hit.
+-   Added `rtcSetBuffer2` function, that additionally gets the number of
+    elements of a buffer. In dynamic scenes, this function allows to
+    quickly change buffer sizes, making it possible to change the number of
+    primitives of a mesh or the number of crease features for subdivision
+    surfaces.
+-   Added simple 'viewer_anim' tutorial for rendering key
+    frame animations and 'buildbench' for measuring BVH (re-)build
+    performance for static and dynamic scenes.
+-   Added more AVX-512 optimizations for future architectures.
+
+### New Features in Embree 2.13.0
+
+-   Improved performance for compact (but not robust) scenes.
+-   Added robust mode for motion blurred triangles and quads.
+-   Added fast dynamic mode for user geometries.
+-   Up to 20% faster BVH build performance on the second generation
+    Intel® Xeon Phi™ processor codenamed Knights Landing.
+-   Improved quality of the spatial split builder.
+-   Improved performance for coherent streams of ray packets (SOA layout),
+    e.g. for fast primary visibility.
+-   Various bug fixes in tessellation cache, quad-based spatial
+    split builder, etc.
+
+### New Features in Embree 2.12.0
+
+-   Added support for multi-segment motion blur for all primitive types.
+-   API support for stream of pointers to single rays (`rtcIntersect1Mp`
+    and `rtcOccluded1Mp`)
+-   Improved BVH refitting performance for dynamic scenes.
+-   Improved high-quality mode for quads (added spatial split builder
+    for quads)
+-   Faster dynamic scenes for triangle and quad-based meshes on AVX2
+    enabled machines.
+-   Performance and correctness bugfix in optimization for streams of
+    coherent (single) rays.
+-   Fixed large memory consumption (issue introduced in Embree v2.11.0).
+    If you use Embree v2.11.0 please upgrade to Embree v2.12.0.
+-   Reduced memory consumption for dynamic scenes containing small
+    meshes.
+-   Added support to start and affinitize TBB worker threads by passing
+    "`start_threads=1,set_affinity=1`" to `rtcNewDevice`. These settings
+    are recommended on systems with a high thread count.
+-   `rtcInterpolate2` can now be called within a displacement shader.
+-   Added initial support for Microsoft's Parallel Pattern Library (PPL)
+    as tasking system alternative (for optimal performance TBB is
+    highly recommended).
+-   Updated to TBB 2017 which is released under the Apache v2.0 license.
+-   Dropped support for Visual Studio 2012 Win32 compiler. Visual Studio
+    2012 x64 is still supported.
+
+### New Features in Embree 2.11.0
+
+-   Improved performance for streams of coherent (single) rays flagged
+    with `RTC_INTERSECT_COHERENT`. For such coherent ray streams, e.g.
+    primary rays, the performance typically improves by 1.3–2×.
+-   New spatial split BVH builder for triangles, which is 2–6× faster
+    than the previous version and more memory conservative.
+-   Improved performance and scalability of all standard BVH builders on
+    systems with large core counts.
+-   Fixed `rtcGetBounds` for motion blur scenes.
+-   Thread affinity is now on by default when running on the latest
+    Intel® Xeon Phi™ processor.
+-   Added AVX-512 support for future Intel® Xeon processors.
+
+### New Features in Embree 2.10.0
+
+-   Added a new curve geometry which renders the sweep surface of a
+    circle along a Bézier curve.
+-   Intersection filters can update the `tfar` ray distance.
+-   Geometry types can get disabled at compile time.
+-   Modified and extended the ray stream API.
+-   Added new callback mechanism for the ray stream API.
+-   Improved ray stream performance (up to 5–10%).
+-   Up to 20% faster morton builder on machines with large core counts.
+-   Lots of optimizations for the second generation Intel® Xeon Phi™
+    processor codenamed Knights Landing.
+-   Added experimental support for compressed BVH nodes (reduces node
+    size to 56–62% of uncompressed size). Compression introduces a
+    typical performance overhead of ~10%.
+-   Bugfix in backface culling mode. We do now properly cull the
+    backfaces and not the frontfaces.
+-   Feature freeze for the first generation Intel® Xeon Phi™ coprocessor
+    codenamed Knights Corner. We will still maintain and add bug fixes
+    to Embree v2.9.0, but Embree 2.10 and future versions will no longer
+    support it.
+
+### New Features in Embree 2.9.0
+
+-   Improved shadow ray performance (10–100% depending on the scene).
+-   Added initial support for ray streams (10–30% higher performance
+    depending on ray coherence in the stream).
+-   Added support to calculate second order derivatives using the
+    `rtcInterpolate2` function.
+-   Changed the parametrization for triangular subdivision faces to
+    the same scheme used for pentagons.
+-   Added support to query the Embree configuration using the
+    `rtcDeviceGetParameter` function.
+
+### New Features in Embree 2.8.1
+
+-   Added support for setting per geometry tessellation rate (supported
+    for subdivision and Bézier geometries).
+-   Added support for motion blurred instances.
+
+### New Features in Embree 2.8.0
+
+-   Added support for line segment geometry.
+-   Added support for quad geometry (replaces triangle-pairs feature).
+-   Added support for linear motion blur of user geometries.
+-   Improved performance through AVX-512 optimizations.
+-   Improved performance of lazy scene build (when using TBB 4.4 update 2).
+-   Improved performance through huge page support under linux.
+
+### New Features in Embree 2.7.1
+
+-   Internal tasking system supports cancellation of build operations.
+-   ISPC mode for robust and compact scenes got significantly faster
+    (implemented hybrid traversal for bvh4.triangle4v and bvh4.triangle4i).
+-   Hair rendering got faster as we fixed some issues with the SAH
+    heuristic cost factors.
+-   BVH8 got slight faster for single ray traversal (improved sorting
+    when hitting more than 4 boxes).
+-   BVH build performance got up to 30% faster on CPUs with high core
+    counts (improved parallel partition code).
+-   High quality build mode again working properly (spatial splits had been
+    deactivated in v2.7.0 due to some bug).
+-   Support for merging two adjacent triangles sharing a common edge
+    into a triangle-pair primitive (can reduce memory consumption and
+    BVH build times by up to 50% for mostly quad-based input meshes).
+-   Internal cleanups (reduced number of traversal kernels by more templating)
+-   Reduced stack size requirements of BVH builders.
+-   Fixed crash for dynamic scenes, triggered by deleting all
+    geometries from the scene.
+
+### New Features in Embree 2.7.0
+
+-   Added device concept to Embree to allow different components of an
+    application to use Embree without interfering with each other.
+-   Fixed memory leak in twolevel builder used for dynamic scenes.
+-   Fixed bug in tesselation cache that caused crashes for subdivision
+    surfaces.
+-   Fixed bug in internal task scheduler that caused deadlocks when
+    using `rtcCommitThread`.
+-   Improved hit-distance accuracy for thin triangles in robust mode.
+-   Added support to disable ray packet support in cmake.
+
+### New Features in Embree 2.6.2
+
+-   Fixed bug triggered by instantiating motion blur geometry.
+-   Fixed bug in hit UV coordinates of static subdivision geometries.
+-   Performance improvements when only changing tessellation levels for
+    subdivision geometry per frame.
+-   Added ray packet intersectors for subdivision geometry, resulting in
+    improved performance for coherent rays.
+-   Reduced virtual address space usage for static geometries.
+-   Fixed some AVX2 code paths when compiling with GCC or Clang.
+-   Bugfix for subdiv patches with non-matching winding order.
+-   Bugfix in ISA detection of AVX-512.
+
+### New Features in Embree 2.6.1
+
+-   Major performance improvements for ray tracing subdivision surfaces,
+    e.g. up to 2× faster for scenes where only the tessellation levels
+    are changing per frame, and up to 3× faster for scenes with lots of
+    crease features
+-   Initial support for architectures supporting the new 16-wide AVX-512
+    ISA
+-   Implemented intersection filter callback support for subdivision
+    surfaces
+-   Added `RTC_IGNORE_INVALID_RAYS` CMake option which makes the ray
+    intersectors more robust against full tree traversal caused by
+    invalid ray inputs (e.g. INF, NaN, etc)
+
+### New Features in Embree 2.6.0
+
+-   Added `rtcInterpolate` function to interpolate per vertex
+    attributes
+-   Added `rtcSetBoundaryMode` function that can be used to select the
+    boundary handling for subdivision surfaces
+-   Fixed a traversal bug that caused rays with very small ray
+    direction components to miss geometry
+-   Performance improvements for the robust traversal mode
+-   Fixed deadlock when calling `rtcCommit` from multiple
+    threads on same scene
+
+### New Features in Embree 2.5.1
+
+-   On dual socket workstations, the initial BVH build performance
+    almost doubled through a better memory allocation scheme
+-   Reduced memory usage for subdivision surface objects with crease
+    features
+-   `rtcCommit` performance is robust against unset "flush to zero" and
+    "denormals are zero" flags. However, enabling these flags in your
+    application is still recommended
+-   Reduced memory usage for subdivision surfaces with borders and
+    infinitely sharp creases
+-   Lots of internal cleanups and bug fixes for both Intel® Xeon® and
+    Intel® Xeon Phi™
+
+### New Features in Embree 2.5.0
+
+-   Improved hierarchy build performance on both Intel Xeon and Intel
+    Xeon Phi
+-   Vastly improved tessellation cache for ray tracing subdivision
+    surfaces
+-   Added `rtcGetUserData` API call to query per geometry user pointer
+    set through `rtcSetUserData`
+-   Added support for memory monitor callback functions to track and
+    limit memory consumption
+-   Added support for progress monitor callback functions to track build
+    progress and cancel long build operations
+-   BVH builders can be used to build user defined hierarchies inside
+    the application (see tutorial [BVH Builder])
+-   Switched to TBB as default tasking system on Xeon to get even faster
+    hierarchy build times and better integration for applications that
+    also use TBB
+-   `rtcCommit` can get called from multiple TBB threads to join the
+    hierarchy build operations
+
+### New Features in Embree 2.4
+
+-   Support for Catmull Clark subdivision surfaces (triangle/quad base
+    primitives)
+-   Support for vector displacements on Catmull Clark subdivision
+    surfaces
+-   Various bug fixes (e.g. 4-byte alignment of vertex buffers works)
+
+### New Features in Embree 2.3.3
+
+-   BVH builders more robustly handle invalid input data (Intel Xeon
+    processor family)
+-   Motion blur support for hair geometry (Xeon)
+-   Improved motion blur performance for triangle geometry (Xeon)
+-   Improved robust ray tracing mode (Xeon)
+-   Added `rtcCommitThread` API call for easier integration into
+    existing tasking systems (Xeon and Intel Xeon Phi coprocessor)
+-   Added support for recording and replaying all
+    `rtcIntersect`/`rtcOccluded` calls (Xeon and Xeon Phi)
+
+### New Features in Embree 2.3.2
+
+-   Improved mixed AABB/OBB-BVH for hair geometry (Xeon Phi)
+-   Reduced amount of pre-allocated memory for BVH builders (Xeon Phi)
+-   New 64 bit Morton code-based BVH builder (Xeon Phi)
+-   (Enhanced) Morton code-based BVH builders use now tree rotations to
+    improve BVH quality (Xeon Phi)
+-   Bug fixes (Xeon and Xeon Phi)
+
+### New Features in Embree 2.3.1
+
+-   High quality BVH mode improves spatial splits which result in up to
+    30% performance improvement for some scenes (Xeon)
+-   Compile time enabled intersection filter functions do not reduce
+    performance if no intersection filter is used in the scene (Xeon and
+    Xeon Phi)
+-   Improved ray tracing performance for hair geometry by \>20% on Xeon
+    Phi. BVH for hair geometry requires 20% less memory
+-   BVH8 for AVX/AVX2 targets improves performance for single ray
+    tracing on Haswell by up to 12% and by up to 5% for hybrid (Xeon)
+-   Memory conservative BVH for Xeon Phi now uses BVH node quantization
+    to lower memory footprint (requires half the memory footprint of the
+    default BVH)
+
+### New Features in Embree 2.3
+
+-   Support for ray tracing hair geometry (Xeon and Xeon Phi)
+-   Catching errors through error callback function
+-   Faster hybrid traversal (Xeon and Xeon Phi)
+-   New memory conservative BVH for Xeon Phi
+-   Faster Morton code-based builder on Xeon
+-   Faster binned-SAH builder on Xeon Phi
+-   Lots of code cleanups/simplifications/improvements (Xeon and Xeon
+    Phi)
+
+### New Features in Embree 2.2
+
+-   Support for motion blur on Xeon Phi
+-   Support for intersection filter callback functions
+-   Support for buffer sharing with the application
+-   Lots of AVX2 optimizations, e.g. \~20% faster 8-wide hybrid
+    traversal
+-   Experimental support for 8-wide (AVX/AVX2) and 16-wide BVHs (Xeon
+    Phi)
+
+### New Features in Embree 2.1
+
+-   New future proof API with a strong focus on supporting dynamic
+    scenes
+-   Lots of optimizations for 8-wide AVX2 (Haswell architecture)
+-   Automatic runtime code selection for SSE, AVX, and AVX2
+-   Support for user-defined geometry
+-   New and improved BVH builders:
+    -   Fast adaptive Morton code-based builder (without SAH-based
+        top-level rebuild)
+    -   Both the SAH and Morton code-based builders got faster (Xeon
+        Phi)
+    -   New variant of the SAH-based builder using triangle pre-splits
+        (Xeon Phi)
+
+### Example Performance Numbers for Embree 2.1
+
+BVH rebuild performance (including triangle accel generation, excluding
+memory allocation) for scenes with 2–12 million triangles:
+
+-   Intel® Core™ i7 (Haswell-based CPU, 4 cores @ 3.0 GHz)
+    -   7–8 million triangles/s for the SAH-based BVH builder
+    -   30–36 million triangles/s for the Morton code-based BVH builder
+-   Intel® Xeon Phi™ 7120
+    -   37–40 million triangles/s for the SAH-based BVH builder
+    -   140–160 million triangles/s for the Morton code-based BVH
+        builder
+
+Rendering of the Crown model (`crown.ecs`) with 4 samples per pixel
+(`-spp 4`):
+
+-   Intel® Core™ i7 (Haswell-based CPU, 4 cores CPU @ 3.0 GHz)
+    -   1024×1024 resolution: 7.8 million rays per sec
+    -   1920×1080 resolution: 9.9 million rays per sec
+-   Intel® Xeon Phi™ 7120
+    -   1024×1024 resolution: 47.1 million rays per sec
+    -   1920×1080 resolution: 61.1 million rays per sec
+
+### New Features in Embree 2.0
+
+-   Support for the Intel® Xeon Phi™ coprocessor platform
+-   Support for high-performance "packet" kernels on SSE, AVX, and Xeon
+    Phi
+-   Integration with the Intel® SPMD Program Compiler (ISPC)
+-   Instantiation and fast BVH reconstruction
+-   Example photo-realistic rendering engine for both C++ and ISPC

+ 355 - 0
Source/ThirdParty/embree/CMakeLists.txt

@@ -0,0 +1,355 @@
+## ======================================================================== ##
+## Copyright 2009-2017 Intel Corporation                                    ##
+##                                                                          ##
+## Licensed under the Apache License, Version 2.0 (the "License");          ##
+## you may not use this file except in compliance with the License.         ##
+## You may obtain a copy of the License at                                  ##
+##                                                                          ##
+##     http://www.apache.org/licenses/LICENSE-2.0                           ##
+##                                                                          ##
+## Unless required by applicable law or agreed to in writing, software      ##
+## distributed under the License is distributed on an "AS IS" BASIS,        ##
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ##
+## See the License for the specific language governing permissions and      ##
+## limitations under the License.                                           ##
+## ======================================================================== ##
+
+SET(EMBREE_VERSION_MAJOR 2)
+SET(EMBREE_VERSION_MINOR 15)
+SET(EMBREE_VERSION_PATCH 1)
+SET(EMBREE_VERSION_NOTE "")
+SET(EMBREE_VERSION ${EMBREE_VERSION_MAJOR}.${EMBREE_VERSION_MINOR}.${EMBREE_VERSION_PATCH})
+MATH(EXPR EMBREE_VERSION_NUMBER "10000*${EMBREE_VERSION_MAJOR} + 100*${EMBREE_VERSION_MINOR} + ${EMBREE_VERSION_PATCH}")
+SET(CPACK_RPM_PACKAGE_RELEASE 1)
+
+PROJECT(embree${EMBREE_VERSION_MAJOR})
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
+
+
+# find git version
+IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
+  FIND_PACKAGE(Git)
+  IF(GIT_FOUND)
+    EXECUTE_PROCESS(
+      COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
+      WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+      OUTPUT_VARIABLE "EMBREE_HASH"
+      ERROR_QUIET
+      OUTPUT_STRIP_TRAILING_WHITESPACE)
+  ELSE()
+    SET(EMBREE_HASH 0)
+  ENDIF()
+ENDIF()
+
+CONFIGURE_FILE(
+  "${PROJECT_SOURCE_DIR}/kernels/version.h.in"
+  "${PROJECT_BINARY_DIR}/version.h"
+)
+FILE(WRITE "${PROJECT_BINARY_DIR}/hash" "${EMBREE_HASH}")
+
+IF(COMMAND cmake_policy)
+  if (POLICY CMP0003)
+    cmake_policy(SET CMP0003 NEW)
+  endif()
+  if (POLICY CMP0042)
+    cmake_policy(SET CMP0042 OLD)
+  endif()
+ENDIF(COMMAND cmake_policy)
+
+MARK_AS_ADVANCED(CMAKE_BACKWARDS_COMPATIBILITY)
+MARK_AS_ADVANCED(EXECUTABLE_OUTPUT_PATH)
+MARK_AS_ADVANCED(LIBRARY_OUTPUT_PATH)
+
+MARK_AS_ADVANCED(CMAKE_OSX_ARCHITECTURES)
+MARK_AS_ADVANCED(CMAKE_OSX_DEPLOYMENT_TARGET)
+MARK_AS_ADVANCED(CMAKE_OSX_SYSROOT)
+MARK_AS_ADVANCED(CLEAR CMAKE_CXX_COMPILER)
+MARK_AS_ADVANCED(GLUT_cocoa_LIBRARY)
+
+SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/common/cmake ${CMAKE_MODULE_PATH})
+
+INCLUDE(test)
+
+##############################################################
+# Embree configuration
+##############################################################
+
+# ATOMIC BEGIN
+OPTION(EMBREE_STATIC_LIB "Build Embree as a static library." ON)
+# ATOMIC END
+IF (EMBREE_STATIC_LIB)
+  SET(EMBREE_LIB_TYPE STATIC)
+  ADD_DEFINITIONS(-DEMBREE_STATIC_LIB)
+ELSE()
+  SET(EMBREE_LIB_TYPE SHARED)
+ENDIF()
+
+# ATOMIC BEGIN
+OPTION(EMBREE_ISPC_SUPPORT "Build Embree with support for ISPC applications." OFF)
+OPTION(EMBREE_TUTORIALS    "Enable to build Embree tutorials" OFF)
+# ATOMIC END
+
+##############################################################################
+# Configurations (add configurations also to common/cmake/embree-config-default.cmake)
+
+OPTION(EMBREE_STAT_COUNTERS "Enables statistic counters.")
+
+OPTION(EMBREE_RAY_MASK "Enables ray mask support.")
+OPTION(EMBREE_BACKFACE_CULLING "Enables backface culling.")
+OPTION(EMBREE_INTERSECTION_FILTER "Enables intersection filter callback." ON)
+OPTION(EMBREE_INTERSECTION_FILTER_RESTORE "Restores previous hit when hit is filtered out." ON)
+OPTION(EMBREE_IGNORE_INVALID_RAYS "Ignores invalid rays." OFF) # FIXME: enable by default?
+
+OPTION(EMBREE_GEOMETRY_TRIANGLES "Enables support for triangle geometries." ON)
+OPTION(EMBREE_GEOMETRY_QUADS "Enables support for quad geometries." ON)
+OPTION(EMBREE_GEOMETRY_LINES "Enables support for line geometries." ON)
+# ATOMIC BEGIN
+OPTION(EMBREE_GEOMETRY_HAIR "Enables support for hair geometries." OFF)
+OPTION(EMBREE_GEOMETRY_SUBDIV "Enables support for subdiv geometries." OFF)
+# ATOMIC END
+OPTION(EMBREE_GEOMETRY_USER "Enables support for user geometries." ON)
+OPTION(EMBREE_RAY_PACKETS "Enabled support for ray packets." ON)
+
+SET(EMBREE_NATIVE_SPLINE_BASIS BEZIER CACHE STRING "Sets the basis for curves which Embree uses internally. Other types are converted and need more memory.")
+SET_PROPERTY(CACHE EMBREE_NATIVE_SPLINE_BASIS PROPERTY STRINGS BSPLINE BEZIER)
+IF (EMBREE_NATIVE_SPLINE_BASIS STREQUAL "BSPLINE")
+  SET(EMBREE_NATIVE_CURVE_BSPLINE ON)
+ELSE()
+  SET(EMBREE_NATIVE_CURVE_BSPLINE OFF)
+ENDIF()
+
+# ATOMIC BEGIN
+# defaulting from TBB to INTERNAL, TODO: look into TBB
+SET(EMBREE_TASKING_SYSTEM "INTERNAL" CACHE STRING "Selects tasking system")
+# ATOMIC END
+
+IF (WIN32)
+  SET_PROPERTY(CACHE EMBREE_TASKING_SYSTEM PROPERTY STRINGS TBB INTERNAL PPL)
+ELSE()
+  SET_PROPERTY(CACHE EMBREE_TASKING_SYSTEM PROPERTY STRINGS TBB INTERNAL)
+ENDIF()
+
+IF (EMBREE_TASKING_SYSTEM STREQUAL "TBB")
+  SET(TASKING_TBB      ON )
+  SET(TASKING_INTERNAL OFF)
+  SET(TASKING_PPL      OFF )
+  ADD_DEFINITIONS(-DTASKING_TBB)
+ELSEIF (EMBREE_TASKING_SYSTEM STREQUAL "PPL")
+  SET(TASKING_PPL      ON )
+  SET(TASKING_TBB      OFF )
+  SET(TASKING_INTERNAL OFF)
+  ADD_DEFINITIONS(-DTASKING_PPL)
+ELSE()
+  SET(TASKING_INTERNAL ON )
+  SET(TASKING_TBB      OFF)
+  SET(TASKING_PPL      OFF )
+  ADD_DEFINITIONS(-DTASKING_INTERNAL)
+ENDIF()
+
+CONFIGURE_FILE(
+  "${PROJECT_SOURCE_DIR}/kernels/config.h.in"
+  "${PROJECT_BINARY_DIR}/config.h"
+)
+
+##############################################################
+# Compiler
+##############################################################
+
+SET(CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo")
+
+IF (WIN32)
+  IF (NOT DEFAULT_CMAKE_CONFIGURATION_TYPES_SET)
+    SET(CMAKE_CONFIGURATION_TYPES "${CONFIGURATION_TYPES}" CACHE STRING "List of generated configurations." FORCE)
+    SET(DEFAULT_CMAKE_CONFIGURATION_TYPES_SET ON CACHE INTERNAL "Default CMake configuration types set.")
+  ENDIF()
+
+  # ATOMIC BEGIN
+  # SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON)
+  # ATOMIC END
+
+  IF (${CMAKE_GENERATOR_TOOLSET} MATCHES "^LLVM" )
+    INCLUDE(clang)
+  ELSE()
+    INCLUDE (msvc)
+  ENDIF()
+
+ELSE (WIN32)
+  IF (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
+    INCLUDE (gcc)
+  ELSEIF (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
+    INCLUDE (clang)
+  ELSEIF (${CMAKE_CXX_COMPILER_ID} STREQUAL "Intel")
+    INCLUDE (icc)
+  ELSE ()
+    MESSAGE(FATAL_ERROR "Unsupported compiler: " ${CMAKE_CXX_COMPILER_ID})
+  ENDIF ()
+
+  IF(NOT CMAKE_BUILD_TYPE)
+    SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Specifies the build type." FORCE)
+    SET_PROPERTY(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${CONFIGURATION_TYPES})
+  ENDIF(NOT CMAKE_BUILD_TYPE)
+
+  OPTION(CMAKE_VERBOSE_MAKEFILE "Enables verbose mode.")
+  MARK_AS_ADVANCED(CLEAR CMAKE_VERBOSE_MAKEFILE)
+
+ENDIF (WIN32)
+
+##############################################################
+# ISA configuration
+##############################################################
+
+# ATOMIC BEGIN
+# Setting to NONE as don't support ISPC
+# macOS is selecting SSE2, look into this on Windows
+SET(EMBREE_MAX_ISA "NONE" CACHE STRING "Selects highest ISA to support.")
+# ATOMIC END
+SET_PROPERTY(CACHE EMBREE_MAX_ISA PROPERTY STRINGS NONE SSE2 SSE4.2 AVX AVX2 AVX512KNL AVX512SKX)
+
+IF (EMBREE_MAX_ISA STREQUAL "NONE")
+  SET(EMBREE_ISA_SSE2  ON)
+  SET(EMBREE_ISA_SSE42 "OFF" CACHE BOOL "Enables SSE4.2 ISA.")
+  SET(EMBREE_ISA_AVX "OFF" CACHE BOOL "Enables AVX ISA.")
+  SET(EMBREE_ISA_AVX2 "OFF" CACHE BOOL "Enables AVX2 ISA.")
+  SET(EMBREE_ISA_AVX512KNL "OFF" CACHE BOOL "Enables AVX512 ISA for Knights Landing.")
+  SET(EMBREE_ISA_AVX512SKX "OFF" CACHE BOOL "Enables AVX512 ISA for Skylake.")
+ELSE()
+  UNSET(EMBREE_ISA_SSE2 CACHE)
+  UNSET(EMBREE_ISA_SSE42 CACHE)
+  UNSET(EMBREE_ISA_AVX CACHE)
+  UNSET(EMBREE_ISA_AVX2 CACHE)
+  UNSET(EMBREE_ISA_AVX512KNL CACHE)
+  UNSET(EMBREE_ISA_AVX512SKX CACHE)
+
+  IF(EMBREE_MAX_ISA STREQUAL "SSE2")
+    SET(ISA  1)
+  ELSEIF(EMBREE_MAX_ISA STREQUAL "SSE4.2")
+    SET(ISA  2)
+  ELSEIF(EMBREE_MAX_ISA STREQUAL "AVX")
+    SET(ISA  3)
+  ELSEIF(EMBREE_MAX_ISA STREQUAL "AVX2")
+    SET(ISA  4)
+  ELSEIF(EMBREE_MAX_ISA STREQUAL "AVX512KNL")
+    SET(ISA  5)
+  ELSEIF(EMBREE_MAX_ISA STREQUAL "AVX512SKX")
+    SET(ISA 6)
+  ELSE()
+    MESSAGE(FATAL_ERROR "Unsupported ISA specified: " ${EMBREE_MAX_ISA})
+  ENDIF()
+
+  SET(EMBREE_ISA_SSE2 OFF)
+  SET(EMBREE_ISA_SSE42 OFF)
+  SET(EMBREE_ISA_AVX OFF)
+  SET(EMBREE_ISA_AVX2 OFF)
+  SET(EMBREE_ISA_AVX512KNL OFF)
+  SET(EMBREE_ISA_AVX512SKX OFF)
+
+  IF (ISA GREATER 0)
+    SET(EMBREE_ISA_SSE2  ON)
+  ENDIF ()
+  IF (ISA GREATER 1)
+    SET(EMBREE_ISA_SSE42  ON)
+  ENDIF ()
+  IF (ISA GREATER 2)
+    SET(EMBREE_ISA_AVX  ON)
+  ENDIF ()
+  IF (ISA GREATER 3)
+    SET(EMBREE_ISA_AVX2  ON)
+  ENDIF ()
+  IF (ISA GREATER 4)
+    SET(EMBREE_ISA_AVX512KNL  ON)
+  ENDIF ()
+  IF (ISA GREATER 5)
+    SET(EMBREE_ISA_AVX512SKX  ON)
+  ENDIF ()
+ENDIF()
+
+IF (EMBREE_ISA_SSE2)
+  ADD_DEFINITIONS(-D__TARGET_SSE2__)
+  LIST(APPEND ISPC_TARGETS "sse2")
+ENDIF()
+
+IF (EMBREE_ISA_SSE42)
+  ADD_DEFINITIONS(-D__TARGET_SSE42__)
+  LIST(APPEND ISPC_TARGETS "sse4")
+ENDIF ()
+
+IF (EMBREE_ISA_AVX)
+  ADD_DEFINITIONS(-D__TARGET_AVX__)
+  LIST(APPEND ISPC_TARGETS "avx")
+ENDIF ()
+
+IF (EMBREE_ISA_AVX2)
+  ADD_DEFINITIONS(-D__TARGET_AVX2__)
+  LIST(APPEND ISPC_TARGETS "avx2")
+ENDIF ()
+
+IF (EMBREE_ISA_AVX512KNL)
+  ADD_DEFINITIONS(-D__TARGET_AVX512KNL__)
+  LIST(APPEND ISPC_TARGETS "avx512knl-i32x16")
+ENDIF ()
+
+IF (EMBREE_ISA_AVX512SKX)
+  ADD_DEFINITIONS(-D__TARGET_AVX512SKX__)
+  LIST(APPEND ISPC_TARGETS "avx512skx-i32x16")
+ENDIF ()
+
+INCLUDE (ispc)
+
+##############################################################
+# Create Binary Packages (uses above config options)
+##############################################################
+# ATOMIC BEGIN
+# include(package)
+# ATOMIC END
+
+##############################################################
+# Search paths
+##############################################################
+INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/include)
+INCLUDE_DIRECTORIES("${PROJECT_BINARY_DIR}")
+INCLUDE_DIRECTORIES_ISPC("${PROJECT_BINARY_DIR}")
+IF (TASKING_TBB)
+  FIND_PACKAGE(TBB REQUIRED)
+  INCLUDE_DIRECTORIES(${TBB_INCLUDE_DIRS})
+ENDIF()
+
+##############################################################
+# Output paths
+##############################################################
+SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+
+##############################################################
+# Directories to compile
+##############################################################
+
+ADD_SUBDIRECTORY(common)
+ADD_SUBDIRECTORY(kernels)
+
+IF (EMBREE_TUTORIALS)
+  ADD_SUBDIRECTORY(tutorials)
+ENDIF()
+
+##############################################################
+# Uninstall
+##############################################################
+
+# ATOMIC BEGIN
+#configure_file(
+#    "${CMAKE_CURRENT_SOURCE_DIR}/common/cmake/uninstall.cmake.in"
+#    "${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake"
+#    IMMEDIATE @ONLY)
+
+#add_custom_target(uninstall
+#    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake)
+
+#SET_PROPERTY(TARGET uninstall PROPERTY FOLDER CMakePredefinedTargets)
+
+# ATOMIC END
+
+##############################################################
+# Has to be last
+##############################################################
+INCLUDE(CPack)

+ 202 - 0
Source/ThirdParty/embree/LICENSE.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 3095 - 0
Source/ThirdParty/embree/README.md

@@ -0,0 +1,3095 @@
+% Embree: High Performance Ray Tracing Kernels 2.15.0
+% Intel Corporation
+
+Embree Overview
+===============
+
+Embree is a collection of high-performance ray tracing kernels,
+developed at Intel. The target user of Embree are graphics application
+engineers that want to improve the performance of their application by
+leveraging the optimized ray tracing kernels of Embree. The kernels
+are optimized for photo-realistic rendering on the latest Intel®
+processors with support for SSE, AVX, AVX2, and AVX-512. Embree
+supports runtime code selection to choose the traversal and build
+algorithms that best matches the instruction set of your CPU. We
+recommend using Embree through its API to get the highest benefit from
+future improvements. Embree is released as Open Source under the
+[Apache 2.0 license](http://www.apache.org/licenses/LICENSE-2.0).
+
+Embree supports applications written with the Intel SPMD Program
+Compiler (ISPC, <https://ispc.github.io/>) by also providing an ISPC
+interface to the core ray tracing algorithms. This makes it possible
+to write a renderer in ISPC that leverages SSE, AVX, AVX2, and AVX-512
+without any code change. ISPC also supports runtime code selection,
+thus ISPC will select the best code path for your application, while
+Embree selects the optimal code path for the ray tracing algorithms.
+
+Embree contains algorithms optimized for incoherent workloads (e.g.
+Monte Carlo ray tracing algorithms) and coherent workloads
+(e.g. primary visibility and hard shadow rays). For standard CPUs, the
+single-ray traversal kernels in Embree provide the best performance
+for incoherent workloads and are very easy to integrate into existing
+rendering applications. For AVX-512 enabled machines, a renderer
+written in ISPC using the default hybrid ray/packet traversal
+algorithms have shown to perform best, but requires writing the
+renderer in ISPC. In general for coherent workloads, ISPC outperforms
+the single ray mode on each platform. Embree also supports dynamic
+scenes by implementing high performance two-level spatial index
+structure construction algorithms.
+
+In addition to the ray tracing kernels, Embree provides some tutorials
+to demonstrate how to use the [Embree API]. The example photorealistic
+renderer that was originally included in the Embree kernel package is
+now available in a separate GIT repository (see [Embree Example
+Renderer]).
+
+Supported Platforms
+-------------------
+
+Embree supports Windows (32 bit and 64 bit), Linux (64 bit) and Mac
+OS X (64 bit). The code compiles with the Intel Compiler, GCC, Clang
+and the Microsoft Compiler.
+
+Using the Intel Compiler improves performance by approximately
+10%. Performance also varies across different operating
+systems, with Linux typically performing best as it supports
+transparently transitioning to 2MB pages.
+
+Embree is optimized for Intel CPUs supporting SSE, AVX, AVX2, and
+AVX-512 instructions, and requires at least a CPU with support for
+SSE2.
+Contributing to Embree
+----------------------
+
+To contribute code to the Embree repository you need to sign a
+Contributor License Agreement (CLA). Individuals need to fill out the
+[Individual Contributor License Agreement (ICLA)]. Corporations need to
+fill out the [Corporate Contributor License Agreement (CCLA)] and each
+employee that wants to contribute has to fill out an [Individual
+Contributor License Agreement (ICLA)]. Please follow the instructions of
+the CLA forms to send them.
+
+Embree Support and Contact
+--------------------------
+
+If you encounter bugs please report them via [Embree's GitHub Issue
+Tracker](https://github.com/embree/embree/issues).
+
+For questions please write us at <[email protected]>.
+
+To receive notifications of updates and new features of Embree please
+subscribe to the [Embree mailing
+list](https://groups.google.com/d/forum/embree/).
+
+Acknowledgements
+----------------
+
+This software is based in part on the work of the Independent JPEG Group.
+
+Installation of Embree
+======================
+
+Windows Installer
+-----------------
+
+You can install the 64 bit version of the Embree library using the
+Windows installer application
+[embree-2.15.0-x64.exe](https://github.com/embree/embree/releases/download/v2.15.0/embree-2.15.0.x64.exe). This
+will install the 64 bit Embree version by default in `Program
+Files\Intel\Embree v2.15.0 x64`. To install the 32 bit
+Embree library use the
+[embree-2.15.0-win32.exe](https://github.com/embree/embree/releases/download/v2.15.0/embree-2.15.0.win32.exe)
+installer. This will install the 32 bit Embree version by default in
+`Program Files\Intel\Embree v2.15.0 win32` on 32 bit
+systems and `Program Files (x86)\Intel\Embree v2.15.0 win32`
+on 64 bit systems.
+
+You have to set the path to the `lib` folder manually to your `PATH`
+environment variable for applications to find Embree. To compile
+applications with Embree you also have to set the `Include
+Directories` path in Visual Studio to the `include` folder of the
+Embree installation.
+
+To uninstall Embree again open `Programs and Features` by clicking the
+`Start button`, clicking `Control Panel`, clicking `Programs`, and
+then clicking `Programs and Features`. Select `Embree
+2.15.0` and uninstall it.
+
+Windows ZIP File
+-----------------
+
+Embree is also delivered as a ZIP file for 64 bit
+[embree-2.15.0.x64.windows.zip](https://github.com/embree/embree/releases/download/v2.15.0/embree-2.15.0.x64.windows.zip)
+and 32 bit
+[embree-2.15.0.win32.windows.zip](https://github.com/embree/embree/releases/download/v2.15.0/embree-2.15.0.win32.windows.zip). After
+unpacking this ZIP file you should set the path to the `lib` folder
+manually to your `PATH` environment variable for applications to find
+Embree. To compile applications with Embree you also have to set the
+`Include Directories` path in Visual Studio to the `include` folder of
+the Embree installation.
+
+If you plan to ship Embree with your application, best use the Embree
+version from this ZIP file.
+
+Linux RPMs
+----------
+
+Uncompress the 'tar.gz' file
+[embree-2.15.0.x86_64.rpm.tar.gz](https://github.com/embree/embree/releases/download/v2.15.0/embree-2.15.0.x86_64.rpm.tar.gz)
+to
+obtain the individual RPM files:
+
+    tar xzf embree-2.15.0.x86_64.rpm.tar.gz
+
+To install the Embree using the RPM packages on your Linux system type
+the following:
+
+    sudo rpm --install embree-lib-2.15.0-1.x86_64.rpm
+    sudo rpm --install embree-devel-2.15.0-1.x86_64.rpm
+    sudo rpm --install embree-examples-2.15.0-1.x86_64.rpm
+
+You also have to install the Intel® Threading Building Blocks (TBB)
+using `yum`:
+
+    sudo yum install tbb.x86_64 tbb-devel.x86_64
+
+or via `apt-get`:
+
+    sudo apt-get install libtbb-dev
+
+Alternatively you can download the latest TBB version from
+[https://www.threadingbuildingblocks.org/download](https://www.threadingbuildingblocks.org/download)
+and set the `LD_LIBRARY_PATH` environment variable to point
+to the TBB library.
+
+Note that the Embree RPMs are linked against the TBB version coming
+with CentOS. This older TBB version is missing some features required
+to get optimal build performance and does not support building of
+scenes lazily during rendering. To get a full featured Embree please
+install using the tar.gz files, which always ship with the latest TBB version.
+
+Under Linux Embree is installed by default in the `/usr/lib` and
+`/usr/include` directories. This way applications will find Embree
+automatically. The Embree tutorials are installed into the
+`/usr/bin/embree2` folder. Specify the full path to
+the tutorials to start them.
+
+To uninstall Embree again just execute the following:
+
+    sudo rpm --erase embree-lib-2.15.0-1.x86_64
+    sudo rpm --erase embree-devel-2.15.0-1.x86_64
+    sudo rpm --erase embree-examples-2.15.0-1.x86_64
+
+Linux tar.gz files
+------------------
+
+The Linux version of Embree is also delivered as a tar.gz file
+[embree-2.15.0.x86_64.linux.tar.gz](https://github.com/embree/embree/releases/download/v2.15.0/embree-2.15.0.x86_64.linux.tar.gz). Unpack
+this file using `tar` and source the provided `embree-vars.sh` (if you
+are using the bash shell) or `embree-vars.csh` (if you are using the
+C shell) to setup the environment properly:
+
+    tar xzf embree-2.15.0.x64.linux.tar.gz
+    source embree-2.15.0.x64.linux/embree-vars.sh
+
+If you want to ship Embree with your application best use the Embree
+version provided through the tar.gz file.
+
+We recommend adding a relative RPATH to your application that points
+to the location Embree (and TBB) can be found, e.g. `$ORIGIN/../lib`.
+
+Mac OS X PKG Installer
+-----------------------
+
+To install the Embree library on your Mac OS X system use the
+provided package installer inside
+[embree-2.15.0.x86_64.dmg](https://github.com/embree/embree/releases/download/v2.15.0/embree-2.15.0.x86_64.dmg). This
+will install Embree by default into `/opt/local/lib` and
+`/opt/local/include` directories. The Embree tutorials are installed
+into the `/Applications/Embree2` folder.
+
+You also have to install the Intel® Threading Building Blocks (TBB)
+using [MacPorts](http://www.macports.org/):
+
+    sudo port install tbb
+
+Alternatively you can download the latest TBB version from
+[https://www.threadingbuildingblocks.org/download](https://www.threadingbuildingblocks.org/download)
+and set the `DYLD_LIBRARY_PATH` environment variable to point
+to the TBB library.
+
+To uninstall Embree again execute the uninstaller script
+`/Applications/Embree2/uninstall.command`.
+
+Mac OS X tar.gz file
+---------------------
+
+The Mac OS X version of Embree is also delivered as a tar.gz file
+[embree-2.15.0.x86_64.macosx.tar.gz](https://github.com/embree/embree/releases/download/v2.15.0/embree-2.15.0.x86_64.macosx.tar.gz). Unpack
+this file using `tar` and source the provided `embree-vars.sh` (if you
+are using the bash shell) or `embree-vars.csh` (if you are using the
+C shell) to setup the environment properly:
+
+    tar xzf embree-2.15.0.x64.macosx.tar.gz
+    source embree-2.15.0.x64.macosx/embree-vars.sh
+
+If you want to ship Embree with your application please use the Embree
+library of the provided tar.gz file. The library name of that Embree
+library is of the form `@rpath/libembree.2.dylib`
+(and similar also for the included TBB library). This ensures that you
+can add a relative RPATH to your application that points to the location
+Embree (and TBB) can be found, e.g. `@loader_path/../lib`.
+
+Linking ISPC applications with Embree
+-------------------------------------
+
+The precompiled Embree library uses the multi-target mode of ISPC. For
+your ISPC application to properly link against Embree you also have to
+enable this mode. You can do this by specifying multiple targets when
+compiling your application with ISPC, e.g.:
+
+    ispc --target sse2,sse4,avx,avx2 -o code.o code.ispc
+
+Compiling Embree
+================
+
+We recommend to use CMake to build Embree. Do not enable fast-math
+optimization, these might break Embree.
+
+Linux and Mac OS X
+-------------------
+
+To compile Embree you need a modern C++ compiler that supports C++11.
+Embree is tested with Intel® Compiler 17.0 (Update 1), Intel®
+Compiler 16.0 (Update 1), Clang 3.8.0 (supports AVX2), Clang 4.0.0
+(supports AVX512) and GCC
+5.4.0. If the GCC that comes with your Fedora/Red Hat/CentOS
+distribution is too old then you can run the provided script
+`scripts/install_linux_gcc.sh` to locally install a recent GCC into
+`$HOME/devtools-2`.
+
+Embree supports to use the Intel® Threading Building Blocks (TBB) as
+tasking system. For performance and flexibility reasons we recommend
+to use Embree with the Intel® Threading Building Blocks (TBB) and best
+also use TBB inside your application. Optionally you can disable TBB
+in Embree through the `EMBREE_TASKING_SYSTEM` CMake variable.
+
+Embree supports the Intel® SPMD Program Compiler (ISPC), which allows
+straight forward parallelization of an entire renderer. If you do not
+want to use ISPC then you can disable `ENABLE_ISPC_SUPPORT` in
+CMake. Otherwise, download and install the ISPC binaries (we have
+tested ISPC version 1.9.1) from
+[ispc.github.io](https://ispc.github.io/downloads.html). After
+installation, put the path to `ispc` permanently into your `PATH`
+environment variable or you need to correctly set the
+`ISPC_EXECUTABLE` variable during CMake configuration.
+
+You additionally have to install CMake 2.8.11 or higher and the developer
+version of GLUT.
+
+Under Mac OS X, all these dependencies can be installed
+using [MacPorts](http://www.macports.org/):
+
+    sudo port install cmake tbb freeglut
+
+Depending on your Linux distribution you can install these dependencies
+using `yum` or `apt-get`.  Some of these packages might already be
+installed or might have slightly different names.
+
+Type the following to install the dependencies using `yum`:
+
+    sudo yum install cmake.x86_64
+    sudo yum install tbb.x86_64 tbb-devel.x86_64
+    sudo yum install freeglut.x86_64 freeglut-devel.x86_64
+    sudo yum install libXmu.x86_64 libXi.x86_64
+    sudo yum install libXmu-devel.x86_64 libXi-devel.x86_64
+
+Type the following to install the dependencies using `apt-get`:
+
+    sudo apt-get install cmake-curses-gui
+    sudo apt-get install libtbb-dev
+    sudo apt-get install freeglut3-dev
+    sudo apt-get install libxmu-dev libxi-dev
+
+Finally you can compile Embree using CMake. Create a build directory
+inside the Embree root directory and execute `ccmake ..` inside this
+build directory.
+
+    mkdir build
+    cd build
+    ccmake ..
+
+Per default CMake will use the compilers specified with the `CC` and
+`CXX` environment variables. Should you want to use a different
+compiler, run `cmake` first and set the `CMAKE_CXX_COMPILER` and
+`CMAKE_C_COMPILER` variables to the desired compiler. For example, to
+use the Intel® Compiler instead of the default GCC on most Linux machines
+(`g++` and `gcc`) execute
+
+    cmake -DCMAKE_CXX_COMPILER=icpc -DCMAKE_C_COMPILER=icc ..
+
+Similarly, to use Clang set the variables to `clang++` and `clang`,
+respectively. Note that the compiler variables cannot be changed anymore
+after the first run of `cmake` or `ccmake`.
+
+Running `ccmake` will open a dialog where you can perform various
+configurations as described below in [CMake Configuration]. After having
+configured Embree, press c (for configure) and g (for generate) to
+generate a Makefile and leave the configuration. The code can be
+compiled by executing make.
+
+    make
+
+The executables will be generated inside the build folder. We recommend
+to finally install the Embree library and header files on your
+system. Therefore set the `CMAKE_INSTALL_PREFIX` to `/usr` in cmake
+and type:
+
+    sudo make install
+
+If you keep the default `CMAKE_INSTALL_PREFIX` of `/usr/local` then
+you have to make sure the path `/usr/local/lib` is in your
+`LD_LIBRARY_PATH`.
+
+You can also uninstall Embree again by executing:
+
+    sudo make uninstall
+
+If you cannot install Embree on your system (e.g. when you don't have
+administrator rights) you need to add embree_root_directory/build to
+your `LD_LIBRARY_PATH`.
+
+
+Windows
+-------
+
+Embree is tested under Windows using the Visual Studio 2017, Visual
+Studio 2015 (Update 1) compiler (Win32 and x64), Visual Studio 2013
+(Update 5) compiler (Win32 and x64), Visual Studio 2012 (Update 4)
+compiler (x64 only), Intel® Compiler 17.0 (Update 1) (Win32 and x64),
+Intel® Compiler 16.0 (Update 1) (Win32 and x64), and Clang 3.9 (Win32
+and x64). Using the Visual Studio 2015 compiler, Visual Studio 2013
+compiler, Intel® Compiler, and Clang you can compile Embree for AVX2,
+while Visual Studio 2012 supports at most AVX. To compile Embree for
+AVX-512 you have to use the Intel® Compiler.
+
+Embree supports to use the Intel® Threading Building Blocks (TBB) as
+tasking system. For performance and flexibility reasons we recommend
+to use Embree with the Intel® Threading Building Blocks (TBB) and best
+also use TBB inside your application. Optionally you can disable TBB
+in Embree through the `EMBREE_TASKING_SYSTEM` CMake variable.
+
+Embree will either find the Intel® Threading Building Blocks (TBB)
+installation that comes with the Intel® Compiler, or you can install the
+binary distribution of TBB directly from
+[www.threadingbuildingblocks.org](https://www.threadingbuildingblocks.org/download)
+into a folder named tbb into your Embree root directory. You also have
+to make sure that the libraries tbb.dll and tbb_malloc.dll can be found when
+executing your Embree applications, e.g. by putting the path to these
+libraries into your `PATH` environment variable.
+
+Embree supports the Intel® SPMD Program Compiler (ISPC), which allows
+straight forward parallelization of an entire renderer. If you do not
+want to use ISPC then you can disable `ENABLE_ISPC_SUPPORT` in
+CMake. Otherwise, download and install the ISPC binaries (we have
+tested ISPC version 1.9.1) from
+[ispc.github.io](https://ispc.github.io/downloads.html). After
+installation, put the path to `ispc.exe` permanently into your `PATH`
+environment variable or you need to correctly set the
+`ISPC_EXECUTABLE` variable during CMake configuration.
+
+You additionally have to install [CMake](http://www.cmake.org/download/)
+(version 2.8.11 or higher). Note that you need a native Windows CMake
+installation, because CMake under Cygwin cannot generate solution files
+for Visual Studio.
+
+### Using the IDE
+
+Run `cmake-gui`, browse to the Embree sources, set the build directory
+and click Configure. Now you can select the Generator, e.g. "Visual
+Studio 12 2013" for a 32 bit build or "Visual Studio 12 2013 Win64"
+for a 64 bit build. If you want to use Clang for compilation, you
+have to specify LLVM-vs2013 as "Optional toolset to use (-T
+parameter)". Most configuration parameters described in the
+[CMake Configuration] can be set under Windows as well. Finally, click
+"Generate" to create the Visual Studio solution files.
+
+  ------------------------- ------------------ ----------------------------
+  Option                    Description        Default
+  ------------------------- ------------------ ----------------------------
+  CMAKE_CONFIGURATION_TYPE  List of generated  Debug;Release;RelWithDebInfo
+                            configurations.
+
+  USE_STATIC_RUNTIME        Use the static     OFF
+                            version of the
+                            C/C++ runtime
+                            library.
+  ------------------------- ------------------ ----------------------------
+  : Windows-specific CMake build options for Embree.
+
+For compilation of Embree under Windows use the generated Visual Studio
+solution file `embree2.sln`. The solution is by default setup to use the
+Microsoft Compiler. You can switch to the Intel® Compiler by right
+clicking onto the solution in the Solution Explorer and then selecting
+the Intel® Compiler. We recommend using 64 bit mode and the Intel®
+Compiler for best performance.
+
+To build Embree with support for the AVX2 instruction set you need at
+least Visual Studio 2013 (Update 4). When switching to the Intel® Compiler
+to build with AVX2 you currently need to manually *remove* the switch
+`/arch:AVX2` from the `embree_avx2` project, which can be found under
+Properties ⇒ C/C++ ⇒ All Options ⇒ Additional Options.
+
+To build all projects of the solution it is recommend to build the CMake
+utility project `ALL_BUILD`, which depends on all projects. Using "Build
+Solution" would also build all other CMake utility projects (such as
+`INSTALL`), which is usually not wanted.
+
+We recommend enabling syntax highlighting for the `.ispc` source and
+`.isph` header files. To do so open Visual Studio, go to Tools ⇒
+Options ⇒ Text Editor ⇒ File Extension and add the isph and ispc
+extension for the "Microsoft Visual C++" editor.
+
+### Using the Command Line
+
+Embree can also be configured and built without the IDE using the Visual
+Studio command prompt:
+
+    cd path\to\embree
+    mkdir build
+    cd build
+    cmake -G "Visual Studio 12 2013 Win64" ..
+    cmake --build . --config Release
+
+To use to the Intel® Compiler set the proper toolset, e.g. for Intel
+Compiler 17.0:
+
+    cmake -G "Visual Studio 12 2013 Win64" -T "Intel C++ Compiler 17.0" ..
+    cmake --build . --config Release
+
+You can also build only some projects with the `--target` switch.
+Additional parameters after "`--`" will be passed to `msbuild`. For
+example, to build the Embree library in parallel use
+
+    cmake --build . --config Release --target embree -- /m
+
+
+CMake Configuration
+-------------------
+
+The default CMake configuration in the configuration dialog should be
+appropriate for most usages. The following table describes all
+parameters that can be configured in CMake:
+
+  ------------------------------ -------------------------------- --------
+  Option                         Description                      Default
+  ------------------------------ -------------------------------- --------
+  CMAKE_BUILD_TYPE               Can be used to switch between    Release
+                                 Debug mode (Debug), Release
+                                 mode (Release), and Release
+                                 mode with enabled assertions
+                                 and debug symbols
+                                 (RelWithDebInfo).
+
+  EMBREE_ISPC_SUPPORT            Enables ISPC support of Embree.  ON
+
+  EMBREE_STATIC_LIB              Builds Embree as a static        OFF
+                                 library. When using the
+                                 statically compiled Embree
+                                 library, you have to define
+                                 ENABLE_STATIC_LIB before
+                                 including rtcore.h in your
+                                 application.
+
+  EMBREE_IGNORE_CMAKE_CXX_FLAGS  When enabled Embree ignores      ON
+                                 default CMAKE_CXX_FLAGS.
+
+  EMBREE_TUTORIALS               Enables build of Embree          ON
+                                 tutorials.
+
+  EMBREE_BACKFACE_CULLING        Enables backface culling, i.e.   OFF
+                                 only surfaces facing a ray can
+                                 be hit.
+
+  EMBREE_INTERSECTION_FILTER     Enables the intersection filter  ON
+                                 feature.
+
+  EMBREE_INTERSECTION_FILTER     Restore previous hit when        ON
+  _RESTORE                       ignoring hits.
+
+  EMBREE_RAY_MASK                Enables the ray masking feature. OFF
+
+  EMBREE_RAY_PACKETS             Enables ray packet support.      ON
+
+  EMBREE_IGNORE_INVALID_RAYS     Makes code robust against the    OFF
+                                 risk of full-tree traversals
+                                 caused by invalid rays (e.g.
+                                 rays containing INF/NaN as
+                                 origins).
+
+  EMBREE_TASKING_SYSTEM          Chooses between Intel® Threading TBB
+                                 Building Blocks (TBB) or an
+                                 internal tasking system
+                                 (INTERNAL).
+
+  EMBREE_MAX_ISA                 Select highest supported ISA     AVX2
+                                 (SSE2, SSE4.2, AVX, AVX2,
+                                 AVX512KNL, AVX512SKX, or NONE).
+                                 When set to NONE the EMBREE_ISA_*
+                                 variables can be used to enable
+                                 ISAs individually.
+
+  EMBREE_ISA_SSE42               Enables SSE4.2 when               OFF
+                                 EMBREE_MAX_ISA is set to NONE.
+
+  EMBREE_ISA_AVX                 Enables AVX when                  OFF
+                                 EMBREE_MAX_ISA is set to NONE.
+
+  EMBREE_ISA_AVX2                Enables AVX2 when                 OFF
+                                 EMBREE_MAX_ISA is set to NONE.
+
+  EMBREE_ISA_AVX512KNL           Enables AVX-512 for Xeon Phi when OFF
+                                 EMBREE_MAX_ISA is set to NONE.
+
+  EMBREE_ISA_AVX512SKX           Enables AVX-512 for Skylake when  OFF
+                                 EMBREE_MAX_ISA is set to NONE.
+
+  EMBREE_GEOMETRY_TRIANGLES      Enables support for triangle      ON
+                                 geometries.
+
+  EMBREE_GEOMETRY_QUADS          Enables support for quad          ON
+                                 geometries.
+
+  EMBREE_GEOMETRY_LINES          Enables support for line          ON
+                                 geometries.
+
+  EMBREE_GEOMETRY_HAIR           Enables support for hair          ON
+                                 geometries.
+
+  EMBREE_GEOMETRY_SUBDIV         Enables support for subdiv        ON
+                                 geometries.
+
+  EMBREE_GEOMETRY_USER           Enables support for user          ON
+                                 geometries.
+
+  EMBREE_NATIVE_SPLINE_BASIS     Specifies the spline basis        BEZIER
+                                 Embree uses internally for its
+                                 calculations. Hair and curves
+                                 using this basis can be rendered
+                                 directly, others are converted.
+
+  ---------------------------- -------------------------------- --------
+  : CMake build options for Embree.
+
+Embree API
+==========
+
+The Embree API is a low level ray tracing API that supports defining
+and committing of geometry and performing ray queries of different
+types. Static and dynamic scenes are supported, that may contain
+triangle geometries, quad geometries, line segment geometries, hair
+geometries, analytic bezier curves, subdivision meshes, instanced
+geometries, and user defined geometries. For each geometry type
+multi-segment motion blur is supported, including support for
+transformation motion blur of instances. Supported ray queries are,
+finding the closest scene intersection along a ray, and testing a ray
+segment for any intersection with the scene. Single rays, as well as
+packets of rays in a struct of array layout can be used for packet
+sizes of 1, 4, 8, and 16 rays. Using the ray stream interface a stream
+of an arbitrary number `M` of ray packets of arbitrary size `N` can be
+processed. Filter callback functions are supported, that get invoked
+for every intersection encountered during traversal.
+
+The Embree API exists in a C++ and ISPC version. This document
+describes the C++ version of the API, the ISPC version is almost
+identical. The only differences are that the ISPC version needs some
+ISPC specific `uniform` type modifiers, and has special functions that
+operate on ray packets of the native SIMD size the ISPC code is
+compiled for.
+
+Embree supports two modes for a scene, the `normal mode` and `stream
+mode`, which require different ray queries and callbacks to be
+used. The `normal mode` is the default, but we will switch entirely to
+the ray `stream mode` in a later release.
+
+The user is supposed to include the `embree2/rtcore.h`, and the
+`embree2/rtcore_ray.h` file, but none of the other header files. If
+using the ISPC version of the API, the user should include
+`embree2/rtcore.isph` and `embree2/rtcore_ray.isph`.
+
+    #include <embree2/rtcore.h>
+    #include <embree2/rtcore_ray.h>
+
+All API calls carry the prefix `rtc` which stands for **r**ay
+**t**racing **c**ore. Embree supports a device concept, which allows
+different components of the application to use the API without
+interfering with each other. You have to create at least one Embree
+device through the `rtcNewDevice` call. Before the application exits
+it should delete all devices by invoking
+`rtcDeleteDevice`. An application typically creates a single device
+only, and should create only a small number of devices.
+
+    RTCDevice device = rtcNewDevice(NULL);
+    ...
+    rtcDeleteDevice(device);
+
+It is strongly recommended to have the `Flush to Zero` and `Denormals
+are Zero` mode of the MXCSR control and status register enabled for
+each thread before calling the `rtcIntersect` and `rtcOccluded`
+functions. Otherwise, under some circumstances special handling of
+denormalized floating point numbers can significantly reduce
+application and Embree performance. When using Embree together with
+the Intel® Threading Building Blocks, it is sufficient to execute the
+following code at the beginning of the application main thread (before
+the creation of the `tbb::task_scheduler_init` object):
+
+    #include <xmmintrin.h>
+    #include <pmmintrin.h>
+    ...
+    _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+    _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
+
+Embree processes some implementation specific configuration from the
+following locations in the specified order:
+
+1) configuration string passed to the `rtcNewDevice` function
+2) `.embree2` file in the application folder
+3) `.embree2` file in the home folder
+
+Settings performed later overwrite previous settings. This way the
+configuration for the application can be changed globally (either
+through the `rtcNewDevice` call or through the `.embree2` file in the
+application folder) and each user has the option to modify the
+configuration to fit its needs. Configuration files can be ignored by
+the application by passing `ignore_config_files=1` to `rtcNewDevice`.
+
+API calls that access geometries are only thread safe as long as
+different geometries are accessed. Accesses to one geometry have to
+get sequenced by the application. All other API calls are thread
+safe. The API calls are re-entrant, it is thus safe to trace new rays
+and create new geometry when intersecting a user defined object.
+
+Each user thread has its own error flag per device. If an error occurs
+when invoking some API function, this flag is set to an error code if it
+stores no previous error. The `rtcDeviceGetError` function reads and returns
+the currently stored error and clears the error flag again.
+
+Possible error codes returned by `rtcDeviceGetError` are:
+
+  ----------------------- ---------------------------------------------
+  Error Code              Description
+  ----------------------- ---------------------------------------------
+  RTC_NO_ERROR            No error occurred.
+
+  RTC_UNKNOWN_ERROR       An unknown error has occurred.
+
+  RTC_INVALID_ARGUMENT    An invalid argument was specified.
+
+  RTC_INVALID_OPERATION   The operation is not allowed for the
+                          specified object.
+
+  RTC_OUT_OF_MEMORY       There is not enough memory left to complete
+                          the operation.
+
+  RTC_UNSUPPORTED_CPU     The CPU is not supported as it does not
+                          support SSE2.
+
+  RTC_CANCELLED           The operation got cancelled
+                          by an Memory Monitor Callback or
+                          Progress Monitor Callback function.
+  ----------------------- ---------------------------------------------
+  : Return values of `rtcDeviceGetError`.
+
+When the device construction fails `rtcNewDevice` returns `NULL` as
+device. To detect the error code of a such a failed device
+construction pass `NULL` as device to the `rtcDeviceGetError`
+function. For all other invokations of `rtcDeviceGetError` a proper
+device pointer has to get specified.
+
+Using the `rtcDeviceSetErrorFunction2` call, it is also possible to
+set a callback function that is called whenever an error occurs for a
+device.
+
+    typedef void (*RTCErrorFunc2)(void* userPtr, const RTCError code, const char* str);
+    void rtcDeviceSetErrorFunction2(RTCDevice device, RTCErrorFunc2 func, void* userPtr);
+
+When invoked, the registred callback function gets passed a user
+defined pointer `userPtr`, the error code `code`, as well as some
+string `str` that describes the error further. Passing `NULL` as
+function pointer to `rtcDeviceSetErrorFunction2` disables the set
+callback function again. The previously described error flags are also
+set if an error callback function is present.
+
+Scene
+-----
+
+A scene is a container for a set of geometries of potentially different
+types. A scene is created using the `rtcDeviceNewScene` function call, and
+destroyed using the `rtcDeleteScene` function call. Two types of scenes
+are supported, dynamic and static scenes. Different flags specify the
+type of scene to create and the type of ray query operations that can
+later be performed on the scene. The following example creates a scene
+that supports dynamic updates and the single ray `rtcIntersect` and
+`rtcOccluded` calls.
+
+    RTCScene scene = rtcDeviceNewScene(device, RTC_SCENE_DYNAMIC, RTC_INTERSECT1);
+    ...
+    rtcDeleteScene(scene);
+
+Using the following scene flags the user can select between creating a
+static or dynamic scene.
+
+  Scene Flag          Description
+  ------------------- ------------------------------------------
+  RTC_SCENE_STATIC    Scene is optimized for static geometry.
+  RTC_SCENE_DYNAMIC   Scene is optimized for dynamic geometry.
+  ------------------- ------------------------------------------
+  : Dynamic type flags for `rtcDeviceNewScene`.
+
+A dynamic scene is created by invoking `rtcDeviceNewScene` with the
+`RTC_SCENE_DYNAMIC` flag. Different geometries can now be created
+inside that scene. Geometries are enabled by default. Once the scene
+geometry is specified, an `rtcCommit` call will finish the scene
+description and trigger building of internal data structures. After
+the `rtcCommit` call it is safe to perform ray queries of the type
+specified at scene construction time. Geometries can get disabled
+(`rtcDisable` call), enabled again (`rtcEnable` call), and deleted
+(`rtcDeleteGeometry` call). Geometries can also get modified,
+including their vertex and index arrays. After the modification of
+some geometry, `rtcUpdate` or `rtcUpdateBuffer` has to get called for
+that geometry to specify which buffers got modified. Each modified
+buffer can be specified separately using the `rtcUpdateBuffer`
+function. In contrast the `rtcUpdate` function simply tags each buffer
+of some geometry as modified. If geometries got enabled, disabled,
+deleted, or modified an `rtcCommit` call has to get invoked before
+performing any ray queries for the scene, otherwise the effect of the
+ray query is undefined. During an `rtcCommit` call modifications to
+the scene are not allowed.
+
+A static scene is created by the `rtcDeviceNewScene` call with the
+`RTC_SCENE_STATIC` flag. Geometries can only get created, enabled,
+disabled and modified until the first `rtcCommit` call. After the
+`rtcCommit` call, each access to any geometry of that static scene is
+invalid. Geometries that got created inside a static scene can only
+get deleted by deleting the entire scene.
+
+The modification of geometry, building of hierarchies using
+`rtcCommit`, and tracing of rays have always to happen separately,
+never at the same time.
+
+Embree silently ignores primitives that would cause numerical issues,
+e.g. primitives containing NaNs, INFs, or values greater
+than 1.844E18f.
+
+The following flags can be used to tune the used acceleration structure.
+These flags are only hints and may be ignored by the implementation.
+
+  ------------------------ ---------------------------------------------
+  Scene Flag               Description
+  ------------------------ ---------------------------------------------
+  RTC_SCENE_COMPACT        Creates a compact data structure and avoids
+                           algorithms that consume much memory.
+
+  RTC_SCENE_COHERENT       Optimize for coherent rays (e.g. primary
+                           rays).
+
+  RTC_SCENE_INCOHERENT     Optimize for in-coherent rays (e.g. diffuse
+                           reflection rays).
+
+  RTC_SCENE_HIGH_QUALITY   Build higher quality spatial data structures.
+  ------------------------ ---------------------------------------------
+  : Acceleration structure flags for `rtcDeviceNewScene`.
+
+The following flags can be used to tune the traversal algorithm that is
+used by Embree. These flags are only hints and may be ignored by the
+implementation.
+
+  Scene Flag         Description
+  ------------------ ----------------------------------------------------
+  RTC_SCENE_ROBUST   Avoid optimizations that reduce arithmetic accuracy.
+  ------------------ ----------------------------------------------------
+  : Traversal algorithm flags for `rtcDeviceNewScene`.
+
+The second argument of the `rtcDeviceNewScene` function are algorithm flags,
+that allow to specify which ray queries are required by the application.
+Calling a ray query API function for a scene that is different to the
+ones specified at scene creation time is not allowed. Further, the
+application should only pass ray query requirements that are really
+needed, to give Embree most freedom in choosing the best algorithm. E.g.
+in case Embree implements no packet traversers for some highly optimized
+data structure for single rays, then this data structure cannot be used
+if the user enables any ray packet query.
+
+  --------------------- ----------------------------------------------------
+  Algorithm Flag        Description
+  --------------------- ----------------------------------------------------
+  RTC_INTERSECT1        Enables the `rtcIntersect` and `rtcOccluded`
+                        functions (single ray interface) for this scene.
+
+  RTC_INTERSECT4        Enables the `rtcIntersect4` and `rtcOccluded4`
+                        functions (4-wide packet interface) for this scene.
+
+  RTC_INTERSECT8        Enables the `rtcIntersect8` and `rtcOccluded8`
+                        functions (8-wide packet interface) for this scene.
+
+  RTC_INTERSECT16       Enables the `rtcIntersect16` and `rtcOccluded16`
+                        functions (16-wide packet interface) for this
+                        scene.
+
+  RTC_INTERSECT_STREAM  Enables the `rtcIntersect1M`, `rtcOccluded1M`,
+                        `rtcIntersect1Mp`, `rtcOccluded1Mp`,
+                        `rtcIntersectNM`, `rtcOccludedNM`,
+                        `rtcIntersectNp`, and `rtcOccludedNp`
+                        functions for this scene.
+
+  RTC_INTERPOLATE       Enables the `rtcInterpolate` and `rtcInterpolateN`
+                        interpolation functions.
+
+  ----------------- ----------------------------------------------------
+  : Enabled algorithm flags for `rtcDeviceNewScene`.
+
+Embree supports two modes for a scene, the `normal mode` and `stream
+mode`. These modes mainly differ in the kind of callbacks invoked and
+how rays are extended with user data. The normal mode is enabled by
+default, the ray stream mode can be enabled using the
+`RTC_INTERSECT_STREAM` algorithm flag for a scene. Only in ray stream
+mode, the stream API functions `rtcIntersect1M`, `rtcIntersect1Mp`,
+`rtcIntersectNM`, and `rtcIntersectNp` as well as their occlusion
+variants can be used.
+
+The scene bounding box can get read by the function
+`rtcGetBounds(RTCScene scene, RTCBounds& bounds_o)`. This function
+will write the AABB of the scene to `bounds_o`. Time varying bounds
+can be obtained usin the `rtcGetLinearBounds(RTCScene scene,
+RTCBounds* bounds_o)` function. This function will write two AABBs to
+`bounds_o`. Linearly interpolating these bounds to a specific time `t`
+yields bounds that bound the geometry at that time. Invoking these
+functions is only valid when all scene changes got committed using
+`rtcCommit`.
+
+Geometries
+----------
+
+Geometries are always contained in the scene they are created in. Each
+geometry is assigned an integer ID at creation time, which is unique
+for that scene. The current version of the API supports triangle
+meshes (`rtcNewTriangleMesh`), quad meshes (`rtcNewQuadMesh`),
+Catmull-Clark subdivision surfaces (`rtcNewSubdivisionMesh`), curve
+geometries (`rtcNewCurveGeometry`), hair geometries
+(`rtcNewHairGeometry`), single level instances of other scenes
+(`rtcNewInstance2`), and user defined geometries
+(`rtcNewUserGeometry`). The API is designed in a way that easily
+allows adding new geometry types in later releases.
+
+For dynamic scenes, the assigned geometry IDs fulfill the following
+properties. As long as no geometry got deleted, all IDs are assigned
+sequentially, starting from 0. If geometries got deleted, the
+implementation will reuse IDs later on in an implementation dependent
+way. Consequently sequential assignment is no longer guaranteed, but a
+compact range of IDs. These rules allow the application to manage a
+dynamic array to efficiently map from geometry IDs to its own geometry
+representation.
+
+For static scenes, geometry IDs are assigned sequentially starting at 0.
+This allows the application to use a fixed size array to map from
+geometry IDs to its own geometry representation.
+
+Alternatively the application can also use the `void rtcSetUserData
+(RTCScene scene, unsigned geomID, void* ptr)` function to set a user
+data pointer `ptr` to its own geometry representation, and later read
+out this pointer again using the `void* rtcGetUserData (RTCScene
+scene, unsigned geomID)` function.
+
+The following geometry flags can be specified at construction time of
+geometries:
+
+  ------------------------ ---------------------------------------------
+  Geometry Flag            Description
+  ------------------------ ---------------------------------------------
+  RTC_GEOMETRY_STATIC      The geometry is considered static and should get
+                           modified rarely by the application. This
+                           flag has to get used in static scenes.
+
+  RTC_GEOMETRY_DEFORMABLE  The geometry is considered to deform in a
+                           coherent way, e.g. a skinned character. The
+                           connectivity of the geometry has to stay
+                           constant, thus modifying the index array is
+                           not allowed. The implementation is free to
+                           choose a BVH refitting approach for handling
+                           meshes tagged with that flag.
+
+  RTC_GEOMETRY_DYNAMIC     The geometry is considered highly dynamic and
+                           changes frequently, possibly in an
+                           unstructured way. Embree will rebuild data
+                           structures from scratch for this type of
+                           geometry.
+  ------------------------ ---------------------------------------------
+  : Flags for the creation of new geometries.
+
+
+### Triangle Meshes
+
+Triangle meshes are created using the `rtcNewTriangleMesh` function
+call, and potentially deleted using the `rtcDeleteGeometry` function
+call.
+
+The number of triangles, number of vertices, and optionally the number
+of time steps for multi-segment motion blur have to get specified at construction
+time of the mesh. The user can also specify additional flags that
+choose the strategy to handle that mesh in dynamic scenes. The
+following example demonstrates how to create a triangle mesh without
+motion blur:
+
+    unsigned geomID = rtcNewTriangleMesh(scene, geomFlags,
+                                         numTriangles, numVertices, 1);
+
+The triangle indices can be set by mapping and writing to the index
+buffer (`RTC_INDEX_BUFFER`) and the triangle vertices can be set by
+mapping and writing into the vertex buffer (`RTC_VERTEX_BUFFER`). The
+index buffer contains an array of three 32 bit indices, while the
+vertex buffer contains an array of three float values aligned to 16
+bytes. The 4th component of the aligned vertices can be arbitrary. All
+buffers have to get unmapped before an `rtcCommit` call to the scene.
+
+    struct Vertex   { float x, y, z, a; };
+    struct Triangle { int v0, v1, v2; };
+
+    Vertex* vertices = (Vertex*) rtcMapBuffer(scene, geomID, RTC_VERTEX_BUFFER);
+    // fill vertices here
+    rtcUnmapBuffer(scene, geomID, RTC_VERTEX_BUFFER);
+
+    Triangle* triangles = (Triangle*) rtcMapBuffer(scene, geomID, RTC_INDEX_BUFFER);
+    // fill triangle indices here
+    rtcUnmapBuffer(scene, geomID, RTC_INDEX_BUFFER);
+
+Also see tutorial [Triangle Geometry] for an example of how to create
+triangle meshes.
+
+The parametrization of a triangle uses the first vertex `p0` as base
+point, and the vector `p1 - p0` as u-direction and `p2 - p0` as
+v-direction. The following picture additionally illustrates the
+direction the geometry normal is pointing into.
+
+![][imgTriangleUV]
+
+Some texture coordinates `t0,t1,t2` can be linearly interpolated over
+the triangle the following way:
+
+    t_uv = (1-u-v)*t0 + u*t1 + v*t2
+
+### Quad Meshes
+
+Quad meshes are created using the `rtcNewQuadMesh` function
+call, and potentially deleted using the `rtcDeleteGeometry` function
+call.
+
+The number of quads, number of vertices, and optionally the number of
+time steps for multi-segment motion blur have to get specified at
+construction time of the mesh. The user can also specify additional
+flags that choose the strategy to handle that mesh in dynamic
+scenes. The following example demonstrates how to create a quad mesh
+without motion blur:
+
+    unsigned geomID = rtcNewQuadMesh(scene, geomFlags,
+                                     numQuads, numVertices, 1);
+
+The quad indices can be set by mapping and writing to the index
+buffer (`RTC_INDEX_BUFFER`) and the quad vertices can be set by
+mapping and writing into the vertex buffer (`RTC_VERTEX_BUFFER`). The
+index buffer contains an array of four 32 bit indices, while the
+vertex buffer contains an array of three float values aligned to 16
+bytes. The 4th component of the aligned vertices can be arbitrary. All
+buffers have to get unmapped before an `rtcCommit` call to the scene.
+
+    struct Vertex { float x, y, z, a; };
+    struct Quad   { int v0, v1, v2, v3; };
+
+    Vertex* vertices = (Vertex*) rtcMapBuffer(scene, geomID, RTC_VERTEX_BUFFER);
+    // fill vertices here
+    rtcUnmapBuffer(scene, geomID, RTC_VERTEX_BUFFER);
+
+    Quad* quads = (Quad*) rtcMapBuffer(scene, geomID, RTC_INDEX_BUFFER);
+    // fill quad indices here
+    rtcUnmapBuffer(scene, geomID, RTC_INDEX_BUFFER);
+
+A quad is internally handled as a pair of two triangles `v0,v1,v3` and
+`v2,v3,v1`, with the u'/v' coordinates of the second triangle
+corrected by `u = 1-u'` and `v = 1-v'` to produce a quad
+parametrization where u and v go from 0 to 1.
+
+To encode a triangle as a quad just replicate the last triangle vertex
+(`v0,v1,v2` -> `v0,v1,v2,v2`). This way the quad mesh can be used to
+represent a mixed mesh which contains triangles and quads.
+
+### Subdivision Surfaces
+
+Catmull-Clark subdivision surfaces for meshes consisting of faces of
+up to 15 vertices (e.g. triangles, quadrilateral, pentagons, etc.) are
+supported, including support for edge creases, vertex creases, holes,
+non-manifold geometry, and face-varying interpolation.
+
+A subdivision surface is created using the `rtcNewSubdivisionMesh`
+function call, and deleted again using the `rtcDeleteGeometry`
+function call.
+
+     unsigned rtcNewSubdivisionMesh(RTCScene scene,
+                                    RTCGeometryFlags flags,
+                                    size_t numFaces,
+                                    size_t numEdges,
+                                    size_t numVertices,
+                                    size_t numEdgeCreases,
+                                    size_t numVertexCreases,
+                                    size_t numCorners,
+                                    size_t numHoles,
+                                    size_t numTimeSteps);
+
+The number of faces (`numFaces`), edges/indices (`numEdges`), vertices
+(`numVertices`), edge creases (`numEdgeCreases`), vertex creases
+(`numVertexCreases`), holes (`numHoles`), and time steps
+(`numTimeSteps`) have to get specified at construction time.
+
+The following buffers have to get setup by the application: the face
+buffer (`RTC_FACE_BUFFER`) contains the number edges/indices (3 to 15) of
+each of the `numFaces` faces, the index buffer (`RTC_INDEX_BUFFER`)
+contains multiple (3 to 15) 32 bit vertex indices for each face and
+`numEdges` indices in total, the vertex buffer (`RTC_VERTEX_BUFFER`)
+stores `numVertices` vertices as single precision `x`, `y`, `z` floating
+point coordinates aligned to 16 bytes. The value of the 4th float used
+for alignment can be arbitrary.
+
+Optionally the application may fill additional index buffers if
+multiple topologies are required for face-varying interpolation. The
+standard vertex buffers `RTC_VERTEX_BUFFER` are always bound to the
+geometry topology (topology 0) thus use `RTC_INDEX_BUFFER0`. Data
+interpolation may use different topologies as described later.
+
+Optionally, the application can setup the hole buffer (`RTC_HOLE_BUFFER`)
+with `numHoles` many 32 bit indices of faces that should be considered
+non-existing in all topologies.
+
+Optionally, the application can fill the level buffer
+(`RTC_LEVEL_BUFFER`) with a tessellation rate for each or the edges of
+each face, making a total of `numEdges` values. The tessellation level
+is a positive floating point value, that specifies how many quads
+along the edge should get generated during tessellation. If no level
+buffer is specified a level of 1 is used. The maximally supported edge
+level is 4096 and larger levels get clamped to that value. Note that
+some edge may be shared between (typically 2) faces. To guarantee a
+watertight tessellation, the level of these shared edges has to be
+exactly identical. A uniform tessellation rate for an entire
+subdivision mesh can be set by using the
+`rtcSetTessellationRate(RTCScene scene, unsigned geomID, float rate)`
+function. The existance of a level buffer has preference over the
+uniform tessellation rate.
+
+Optionally, the application can fill the sparse edge crease buffers to
+make some edges appear sharper. The edge crease index buffer
+(`RTC_EDGE_CREASE_INDEX_BUFFER`) contains `numEdgeCreases` many pairs
+of 32 bit vertex indices that specify unoriented edges in the
+geometry topology. The edge crease weight buffer
+(`RTC_EDGE_CREASE_WEIGHT_BUFFER`) stores for each of theses crease
+edges a positive floating point weight. The larger this weight, the
+sharper the edge. Specifying a weight of infinity is supported and
+marks an edge as infinitely sharp. Storing an edge multiple times with
+the same crease weight is allowed, but has lower performance. Storing
+an edge multiple times with different crease weights results in
+undefined behavior. For a stored edge (i,j), the reverse direction
+edges (j,i) does not have to get stored, as both are considered the
+same edge. Edge crease features are specified for the geomtetry
+topology, but copied to all other topologies automatically.
+
+Optionally, the application can fill the sparse vertex crease buffers
+to make some vertices appear sharper. The vertex crease index buffer
+(`RTC_VERTEX_CREASE_INDEX_BUFFER`), contains `numVertexCreases` many
+32 bit vertex indices to specify a set of vertices from the geometry
+topology. The vertex crease weight buffer
+(`RTC_VERTEX_CREASE_WEIGHT_BUFFER`) specifies for each of these
+vertices a positive floating point weight. The larger this weight, the
+sharper the vertex. Specifying a weight of infinity is supported and
+makes the vertex infinitely sharp. Storing a vertex multiple times
+with the same crease weight is allowed, but has lower
+performance. Storing a vertex multiple times with different crease
+weights results in undefined behavior. Vertex crease features are
+specified for the geomtetry topology, but copied to all other
+topologies automatically.
+
+Faces with 3 to 15 vertices are supported (triangles, quadrilateral,
+pentagons, etc).
+
+The user can also specify a geometry mask and additional flags that
+choose the strategy to handle that subdivision mesh in dynamic scenes.
+
+The implementation of subdivision surfaces uses an internal software cache,
+which can get configured to some desired size (see [Configuring Embree]).
+
+#### Parametrization
+
+The parametrization of a regular quadrilateral uses the first vertex `p0` as
+base point, and the vector `p1 - p0` as u-direction and `p3 - p0` as
+v-direction. The following picture additionally illustrates the
+direction the geometry normal is pointing into.
+
+![][imgQuadUV]
+
+Some texture coordinates `t0,t1,t2,t3` can be bi-linearly
+interpolated over the quadrilateral the following way:
+
+    t_uv = (1-v)((1-u)*t0 + u*t1) + v*((1-u)*t3 + u*t2)
+
+The parametrization for all other face types where the number of
+vertices is not equal to 4, have a special parametrization where the
+n'th quadrilateral (that would be obtained by a single subdivision
+step) is encoded in the higher order bits of the UV coordinates and
+the local hit location inside this quadrilateral in the lower order
+bits. The following piece of code extracts the sub-patch ID i and UVs
+of this subpatch:
+
+    const unsigned l = floorf(4.0f*U);
+    const unsigned h = floorf(4.0f*V);
+    const unsigned i = 4*h+l;
+    const float u = 2.0f*fracf(4.0f*U);
+    const float v = 2.0f*fracf(4.0f*V);
+
+To smoothly interpolate texture coordinates over the subdivision
+surface we recommend using the `rtcInterpolate2` function, which will
+apply the standard subdivision rules for interpolation and
+automatically take care of the special UV encoding for
+non-quadrilaterals.
+
+#### Face-Varing Data
+
+Face-varying interpolation is supported through multiple topologies
+per subdivision mesh and binding such topologies to user vertex buffers
+to interpolate. This way texture coordinates may use a different
+topology with additional boundaries to construct separate UV regions
+inside one subdivision mesh.
+
+Each such topology consists of an index buffer and subdivision
+mode. Up to 16 topologies are supported, with corresponding index
+buffers `RTC_INDEX_BUFFER0+i`, with i in the range 0 to 15.
+
+Each of the 16 supported user vertex buffers
+`RTC_USER_VERTEX_BUFFER0+j` (j in the range 0 to 15) can be assigned
+to some topology using the `rtcSetIndexBuffer` call:
+
+    void rtcSetIndexBuffer(RTCScene scene, unsigned geomID,
+                           RTCBufferType vertexBuffer, RTCBufferType indexBuffer);
+
+The face buffer (`RTC_FACE_BUFFER`) is shared between all topologies,
+which means that the n'th primitive always has the same number of
+vertices (e.g. being a triangle or a quad) for each topology. However,
+the indices of the topologies themselves may be different.
+
+#### Subdivision Mode
+
+The subdivision modes can be used to force linear interpolation for
+some parts of the subdivision mesh.
+
+  -------------------------- ---------------------------------------------
+  Boundary Mode              Description
+  -------------------------- ---------------------------------------------
+  RTC_SUBDIV_NO_BOUNDARY     Boundary patches are ignored. This way each
+                             rendered patch has a full set of control
+                             vertices.
+
+  RTC_SUBDIV_SMOOTH_BOUNDARY The sequence of boundary control points are
+                             used to generate a smooth B-spline boundary
+                             curve (default mode).
+
+  RTC_SUBDIV_PIN_CORNERS     Corner vertices are pinned to their
+                             location during subdivision.
+
+  RTC_SUBDIV_PIN_BOUNDARY    All vertices at the border are pinned to
+                             their location during subdivision. This way
+                             the boundary is interpolated linearly.
+
+  RTC_SUBDIV_PIN_ALL         All vertices at the border are binned to
+                             their location during subdivision. This way all
+                             patches are linearly interpolated.
+
+  ------------------------ ---------------------------------------------
+  : Subdivision modes supported by Embree.
+
+These modes can be set to each topology separately using the
+`rtcSetSubdivisionMode` API call with the following signature:
+
+    void rtcSetSubdivisionMode(RTCScene scene, unsigned geomID,
+                               unsigned topologyID, RTCSubdivisionMode mode);
+
+These modes are typically used to interpolate face-varying data
+properly. E.g. the topology used to interpolate texture coordinaces
+are typically assigned the `RTC_SUBDIV_PIN_BOUNDARY` mode, to also map
+texels at the border of the texture to the mesh.
+
+Also see tutorial [Subdivision Geometry] for an example of how to create
+subdivision surfaces.
+
+### Line Segment Hair Geometry
+
+Line segments are supported to render hair geometry. A line segment
+consists of a start and end point, and start and end radius. Individual
+line segments are considered to be subpixel sized which allows the
+implementation to approximate the intersection calculation. This in
+particular means that zooming onto one line segment might show geometric
+artifacts.
+
+Line segments are created using the `rtcNewLineSegments` function
+call, and potentially deleted using the `rtcDeleteGeometry` function
+call.
+
+The number of line segments, the number of vertices, and optionally
+the number of time steps for multi-segment motion blur have to get
+specified at construction time of the line segment geometry.
+
+The segment indices can be set by mapping and writing to the index buffer
+(`RTC_INDEX_BUFFER`) and the vertices can be set by mapping and
+writing into the vertex buffer (`RTC_VERTEX_BUFFER`). In case of
+motion blur, the vertex buffers (`RTC_VERTEX_BUFFER0+t`) have to get
+filled for each time step `t`.
+
+The index buffer contains an array of 32 bit indices pointing to the
+ID of the first of two vertices, while the vertex buffer
+stores all control points in the form of a single precision position
+and radius stored in `x`, `y`, `z`, `r` order in memory. The
+radii have to be greater or equal zero. All buffers have to get
+unmapped before an `rtcCommit` call to the scene.
+
+The intersection with the line segment primitive stores the parametric
+hit location along the line segment as `u`-coordinate (range $[0, 1]$;
+`v` is always set to zero). The geometry normal `Ng` is filled with the
+the tangent, i.e. the vector from start to end vertex.
+
+Like for triangle meshes, the user can also specify a geometry mask and
+additional flags that choose the strategy to handle that mesh in dynamic
+scenes.
+
+The following example demonstrates how to create some line segment geometry:
+
+    unsigned geomID = rtcNewLineSegments(scene, geomFlags, numCurves,
+                                         numVertices, 1);
+
+    struct Vertex { float x, y, z, r; };
+
+    Vertex* vertices = (Vertex*) rtcMapBuffer(scene, geomID, RTC_VERTEX_BUFFER);
+    // fill vertices here
+    rtcUnmapBuffer(scene, geomID, RTC_VERTEX_BUFFER);
+
+    int* curves = (int*) rtcMapBuffer(scene, geomID, RTC_INDEX_BUFFER);
+    // fill indices here
+    rtcUnmapBuffer(scene, geomID, RTC_INDEX_BUFFER);
+
+### Spline Hair Geometry
+
+Hair geometries are supported, which consist of multiple hairs
+represented as cubic spline curves with varying radius per control
+point. As spline basis we currently support Bézier splines and
+B-splines. Individual hairs are considered to be subpixel sized which allows
+the implementation to approximate the intersection calculation. This in
+particular means that zooming onto one hair might show geometric
+artifacts.
+
+Hair geometries are created using the `rtcNewBezierHairGeometry` or
+`rtcNewBSplineHairGeometry` function call, and potentially deleted
+using the `rtcDeleteGeometry` function call.
+
+The number of hair curves, the number of vertices, and optionally the
+number of time steps for multi-segment motion blur have to get
+specified at construction time of the hair geometry.
+
+The curve indices can be set by mapping and writing to the index
+buffer (`RTC_INDEX_BUFFER`) and the control vertices can be set by
+mapping and writing into the vertex buffer (`RTC_VERTEX_BUFFER`). In
+case of motion blur, the vertex buffers `RTC_VERTEX_BUFFER0+t` have to
+get filled for each time step.
+
+The index buffer contains an array of 32 bit indices pointing to the
+ID of the first of four control vertices, while the vertex buffer
+stores all control points in the form of a single precision position
+and radius stored in `x`, `y`, `z`, `r` order in memory. The hair
+radii have to be greater or equal zero. All buffers have to get
+unmapped before an `rtcCommit` call to the scene.
+
+The intersection with the hair primitive stores the parametric hit
+location along the hair as `u`-coordinate (range 0 to +1), and the
+normalized distance as the `v`-coordinate (range -1 to +1). The
+geometry normal `Ng` is filled with the the tangent of the bezier
+curve at the hit location on the curve (dPdu).
+
+The implementation may choose to subdivide the Bézier curve into
+multiple cylinders-like primitives. The number of cylinders the curve
+gets subdivided into can be specified per hair geometry through the
+`rtcSetTessellationRate(RTCScene scene, unsigned geomID, float rate)`
+function. By default the tessellation rate for hair curves is 4.
+
+Like for triangle meshes, the user can also specify a geometry mask and
+additional flags that choose the strategy to handle that mesh in dynamic
+scenes.
+
+The following example demonstrates how to create some hair geometry:
+
+    unsigned geomID = rtcNewBezierHairGeometry(scene, geomFlags, numCurves, numVertices);
+
+    struct Vertex { float x, y, z, r; };
+
+    Vertex* vertices = (Vertex*) rtcMapBuffer(scene, geomID, RTC_VERTEX_BUFFER);
+    // fill vertices here
+    rtcUnmapBuffer(scene, geomID, RTC_VERTEX_BUFFER);
+
+    int* curves = (int*) rtcMapBuffer(scene, geomID, RTC_INDEX_BUFFER);
+    // fill indices here
+    rtcUnmapBuffer(scene, geomID, RTC_INDEX_BUFFER);
+
+Also see tutorial [Hair] for an example of how to create and use hair
+geometry.
+
+### Spline Curve Geometry
+
+The spline curve geometry consists of multiple cubic spline curves
+with varying radius per control point. As spline basis we currently
+support Bézier splines and B-splines. The cuve surface is defined as
+the sweep surface of sweeping a varying radius circle tangential along
+the Bézier curve. As a limitation, the radius of the curve has to be
+smaller than the curvature radius of the Bézier curve at each location
+on the curve. In contrast to hair geometry, the curve geometry is
+rendered properly even in closeups.
+
+Curve geometries are created using the `rtcNewBezierCurveGeometry` or
+`rtcNewBSplineCurveGeometry` function call, and potentially deleted
+using the `rtcDeleteGeometry` function call.
+
+The number of Bézier curves, the number of vertices, and optionally
+the number of time steps for multi-segment motion blur have to get
+specified at construction time of the curve geometry.
+
+The curve indices can be set by mapping and writing to the index
+buffer (`RTC_INDEX_BUFFER`) and the control vertices can be set by
+mapping and writing into the vertex buffer (`RTC_VERTEX_BUFFER`). In
+case of motion blur, the vertex buffers `RTC_VERTEX_BUFFER0+t` have to
+get filled for each time step.
+
+The index buffer contains an array of 32 bit indices pointing to the
+ID of the first of four control vertices, while the vertex buffer
+stores all control points in the form of a single precision position
+and radius stored in `x`, `y`, `z`, `r` order in memory. The curve
+radii have to be greater or equal zero. All buffers have to get
+unmapped before an `rtcCommit` call to the scene.
+
+Like for triangle meshes, the user can also specify a geometry mask and
+additional flags that choose the strategy to handle the curves in dynamic
+scenes.
+
+Also see tutorial [Curves] for an example of how to create and use
+Bézier curve geometries.
+
+### User Defined Geometry
+
+User defined geometries make it possible to extend Embree with
+arbitrary types of user defined primitives. This is achieved by
+introducing arrays of user primitives as a special geometry
+type.
+
+User geometries are created using the `rtcNewUserGeometry` function
+call, and potentially deleted using the `rtcDeleteGeometry` function
+call. The the `rtcNewUserGeometry2` function additionally gets a
+`numTimeSteps` parameter, which specifies the number of timesteps for
+multi-segment motion blur.
+
+When creating a user defined geometry, the user has to set a data
+pointer, a bounding function closure (function and user pointer) as
+well as user defined intersect and occluded callback function
+pointers. The bounding function is used to query the bounds of all
+timesteps of a user primitive, while the intersect and occluded
+callback functions are called to intersect the primitive with a ray.
+
+The bounding function to register has the following signature
+
+    typedef void (*RTCBoundsFunc3)(void* userPtr, void* geomUserPtr, size_t id, size_t timeStep, RTCBounds& bounds_o);
+
+and can be registered using the `rtcSetBoundsFunction2` API function:
+
+    rtcSetBoundsFunction3(scene, geomID, userBoundsFunction, userPtr);
+
+When the bounding callback is called, it is passed a user defined
+pointer specified at registration time of the bounds function
+(`userPtr` parameter), the per geometry user data pointer
+(`geomUserPtr` parameter), the ID of the primitive to calculate the
+bounds for (`id` parameter), the time step at which to calculate the
+bounds (`timeStep` parameter) and a memory location to write the
+calculated bound to (`bounds_o` parameter).
+
+The signature of supported user defined intersect and occluded
+function in normal mode is as follows:
+
+    typedef void (*RTCIntersectFunc  ) (                   void* userDataPtr, RTCRay& ray, size_t item);
+    typedef void (*RTCIntersectFunc4 ) (const void* valid, void* userDataPtr, RTCRay4& ray, size_t item);
+    typedef void (*RTCIntersectFunc8 ) (const void* valid, void* userDataPtr, RTCRay8& ray, size_t item);
+    typedef void (*RTCIntersectFunc16) (const void* valid, void* userDataPtr, RTCRay16& ray, size_t item);
+
+The `RTCIntersectFunc` callback function operates on single rays and
+gets passed the user data pointer of the user geometry (`userDataPtr`
+parameter), the ray to intersect (`ray` parameter), and the ID of the
+primitive to intersect (`item` parameter). The
+`RTCIntersectFunc4/8/16` callback functions operate on ray packets of
+size 4, 8 and 16 and additionally get an integer valid mask as input
+(`valid` parameter). The callback functions should not modify any ray
+that is disabled by that valid mask.
+
+In stream mode the following callback function has to get used:
+
+    typedef void (*RTCIntersectFuncN ) (const int*  valid, void* userDataPtr, const RTCIntersectContext* context, RTCRayN* rays, size_t N, size_t item);
+    typedef void (*RTCIntersectFunc1Mp)(                   void* userDataPtr, const RTCIntersectContext* context, RTCRay** rays, size_t M, size_t item);
+
+The `RTCIntersectFuncN` callback function supports ray packets of
+arbitrary size `N`. The `RTCIntersectFunc1Mp` callback function get an
+array of `M` pointers to single rays as input.
+
+The user intersect function should return without modifying the ray
+structure if the user geometry is missed. Whereas, if an intersection
+of the user primitive with the ray segment was found, the intersect
+function has to update the hit information of the ray (`tfar`, `u`,
+`v`, `Ng`, `geomID`, `primID` components).
+
+The user occluded function should also return without modifying the ray
+structure if the user geometry is missed. If the geometry is hit, it
+should set the `geomID` member of the ray to 0.
+
+When performing ray queries using the `rtcIntersect` and `rtcOccluded`
+function, callbacks of type `RTCIntersectFunc` are invoked for user
+geometries. Consequently, an application only operating on single rays
+only has to provide the single ray intersect and occluded
+callbacks. Similar when calling the `rtcIntersect4/8/16` and
+`rtcOccluded4/8/16` functions, the `RTCIntersectFunc4/8/16` callbacks
+of matching packet size and type are called.
+
+If ray stream mode is enabled for the scene only the
+`RTCIntersectFuncN` and `RTCIntersectFunc1Mp` callback can be used. In
+this case specifying an `RTCIntersectFuncN` callback is mandatory and
+the `RTCIntersectFunc1Mp` callback is optional. Trying to set a
+different type of user callback function results in an error.
+
+The following example illustrates creating an array with two user
+geometries:
+
+    int numTimeSteps = 2;
+    struct UserObject { ... };
+
+    void userBoundsFunction(void* userPtr, UserObject* userGeomPtr, size_t i, size_t t, RTCBounds& bounds)
+    {
+        bounds = <bounds of userGeomPtr[i] at time t>;
+    }
+
+    void userIntersectFunction(UserObject* userGeomPtr, RTCRay& ray, size_t i)
+    {
+      if (<ray misses userGeomPtr[i] at time ray.time>)
+        return;
+      <update ray hit information>;
+    }
+
+    void userOccludedFunction(UserObject* userGeomPtr, RTCRay& ray, size_t i)
+    {
+      if (<ray misses userGeomPtr[i] at time ray.time>)
+        return;
+      geomID = 0;
+    }
+
+    ...
+
+    UserObject* userGeomPtr = new UserObject[2];
+    userGeomPtr[0] = ...
+    userGeomPtr[1] = ...
+    unsigned geomID = rtcNewUserGeometry2(scene, 2, numTimeSteps);
+    rtcSetUserData(scene, geomID, userGeomPtr);
+    rtcSetBoundsFunction3(scene, geomID, userBoundsFunction, userPtr);
+    rtcSetIntersectFunction(scene, geomID, userIntersectFunction);
+    rtcSetOccludedFunction(scene, geomID, userOccludedFunction);
+
+See tutorial [User Geometry] for an example of how to use the user
+defined geometries.
+
+### Instances
+
+Embree supports instancing of scenes inside another scene by some
+transformation. As the instanced scene is stored only a single time,
+even if instanced to multiple locations, this feature can be used to
+create very large scenes. Only single level instancing is supported by
+Embree natively, however, multi-level instancing can be implemented
+through user geometries.
+
+Instances are created using the `rtcNewInstance2
+(RTCScene target, RTCScene source, size_t numTimeSteps)` function call, and
+potentially deleted using the `rtcDeleteGeometry` function call. To
+instantiate a scene, one first has to generate the scene `B` to
+instantiate. Now one can add an instance of this scene inside a scene `A`
+the following way:
+
+    unsigned instID = rtcNewInstance2(sceneA, sceneB, 1);
+    rtcSetTransform2(sceneA, instID, RTC_MATRIX_COLUMN_MAJOR, &column_matrix_3x4, 0);
+
+To create some motion blurred instance just pass the number of time
+steps and specify one matrix for each time step:
+
+    unsigned instID = rtcNewInstance2(sceneA, sceneB, 3);
+    rtcSetTransform2(sceneA, instID, RTC_MATRIX_COLUMN_MAJOR, &column_matrix_t0_3x4, 0);
+    rtcSetTransform2(sceneA, instID, RTC_MATRIX_COLUMN_MAJOR, &column_matrix_t1_3x4, 1);
+    rtcSetTransform2(sceneA, instID, RTC_MATRIX_COLUMN_MAJOR, &column_matrix_t2_3x4, 2);
+
+Both scenes have to belong to the same device. One has to call
+`rtcCommit` on scene `B` before one calls `rtcCommit` on scene `A`. When
+modifying scene `B` one has to call `rtcUpdate` for all instances of
+that scene. If a ray hits the instance, then the `geomID` and `primID`
+members of the ray are set to the geometry ID and primitive ID of the
+primitive hit in scene `B`, and the `instID` member of the ray is set to
+the instance ID returned from the `rtcNewInstance2` function.
+
+Some special care has to be taken when using user geometries and
+instances in the same scene. Instantiated user geometries should not
+set the `instID` field of the ray as this field is managed by the
+instancing already. However, non-instantiated user geometries should
+clear the `instID` field to `RTC_INVALID_GEOMETRY_ID`, to later
+distinguish them from instantiated geometries that have the `instID`
+field set.
+
+The `rtcSetTransform2` call can be passed an affine transformation matrix
+with different data layouts:
+
+  ----------------------------------- ----------------------------------
+  Layout                              Description
+  ----------------------------------- ----------------------------------
+  RTC_MATRIX_ROW_MAJOR                The 3×4 float matrix is laid out
+                                      in row major form.
+
+  RTC_MATRIX_COLUMN_MAJOR             The 3×4 float matrix is laid out
+                                      in column major form.
+
+  RTC_MATRIX_COLUMN_MAJOR_ALIGNED16   The 3×4 float matrix is laid out
+                                      in column major form, with each
+                                      column padded by an additional 4th
+                                      component.
+  ----------------------------------- ----------------------------------
+  : Matrix layouts for `rtcSetTransform2`.
+
+Passing homogeneous 4×4 matrices is possible as long as the last row is
+(0, 0, 0, 1). If this homogeneous matrix is laid out in row major form,
+use the `RTC_MATRIX_ROW_MAJOR` layout. If this homogeneous matrix is
+laid out in column major form, use the
+`RTC_MATRIX_COLUMN_MAJOR_ALIGNED16` mode. In both cases, Embree will
+ignore the last row of the matrix.
+
+The transformation passed to `rtcSetTransform2` transforms from the local
+space of the instantiated scene to world space.
+
+See tutorial [Instanced Geometry] for an example of how to use
+instances.
+
+Ray Layout
+-----------
+
+The ray layout to be passed to the ray tracing core is defined in the
+`embree2/rtcore_ray.h` header file. It is up to the user to use the
+ray structures defined in that file, or resemble the exact same binary
+data layout with their own vector classes. The ray layout might change
+with new Embree releases as new features get added, however, will stay
+constant as long as the major Embree release number does not
+change. The ray contains the following data members:
+
+  Member  In/Out  Description
+  ------- ------- ----------------------------------------------------------
+  org     in      ray origin
+  dir     in      ray direction (can be unnormalized)
+  tnear   in      start of ray segment
+  tfar    in/out  end of ray segment, set to hit distance after intersection
+  time    in      time used for multi-segment motion blur [0,1]
+  mask    in      ray mask to mask out geometries
+  Ng      out     unnormalized geometry normal
+  u       out     barycentric u-coordinate of hit
+  v       out     barycentric v-coordinate of hit
+  geomID  out     geometry ID of hit geometry
+  primID  out     primitive ID of hit primitive
+  instID  out     instance ID of hit instance
+  ------- ------- ----------------------------------------------------------
+  : Data fields of a ray.
+
+This structure is in struct of array layout (SOA) for API functions
+accepting ray packets.
+
+To create a single ray you can use the `RTCRay` ray type defined in
+`embree2/rtcore_ray.h`. To generate a ray packet of size 4, 8, or 16
+you can use the `RTCRay4`, `RTCRay8`, or `RTCRay16`
+types. Alternatively you can also use the `RTCRayNt` template to
+generate ray packets of an arbitrary compile time known size.
+
+When the ray packet size is not known at compile time (e.g. when
+Embree returns a ray packet in the `RTCFilterFuncN` callback function),
+then you can use the helper functions defined in
+`embree2/rtcore_ray.h` to access ray packet components:
+
+    float& RTCRayN_org_x(RTCRayN* rays, size_t N, size_t i);
+    float& RTCRayN_org_y(RTCRayN* rays, size_t N, size_t i);
+    float& RTCRayN_org_z(RTCRayN* rays, size_t N, size_t i);
+
+    float& RTCRayN_dir_x(RTCRayN* rays, size_t N, size_t i);
+    float& RTCRayN_dir_y(RTCRayN* rays, size_t N, size_t i);
+    float& RTCRayN_dir_z(RTCRayN* rays, size_t N, size_t i);
+
+    float& RTCRayN_tnear(RTCRayN* rays, size_t N, size_t i);
+    float& RTCRayN_tnear(RTCRayN* rays, size_t N, size_t i);
+
+    float&    RTCRayN_time(RTCRayN* ptr, size_t N, size_t i);
+    unsigned& RTCRayN_mask(RTCRayN* ptr, size_t N, size_t i);
+
+    float& RTCRayN_Ng_x(RTCRayN* ptr, size_t N, size_t i);
+    float& RTCRayN_Ng_y(RTCRayN* ptr, size_t N, size_t i);
+    float& RTCRayN_Ng_z(RTCRayN* ptr, size_t N, size_t i);
+
+    float& RTCRayN_u   (RTCRayN* ptr, size_t N, size_t i);
+    float& RTCRayN_v   (RTCRayN* ptr, size_t N, size_t i);
+
+    unsigned& RTCRayN_instID(RTCRayN* ptr, size_t N, size_t i);
+    unsigned& RTCRayN_geomID(RTCRayN* ptr, size_t N, size_t i);
+    unsigned& RTCRayN_primID(RTCRayN* ptr, size_t N, size_t i);
+
+These helper functions get a pointer to the ray packet (`rays`
+parameter), the packet size `N`, and returns a reference to some
+component (e.g. x-component of origin) of the the ith ray of the
+packet.
+
+Please note that there is some incompatibility in the layout of a
+single ray (`RTCRay` type) and a ray packet of size 1 (`RTCRayNt<1>`
+type) as the `org` and `dir` component are aligned to 16 bytes for
+single rays (see `embree2/rtcore_ray.h`). This incompatibility will
+get resolved in a future release, but has to be maintained for
+compatibility currently. Until then, the ray stream API will always
+use the single ray layout `RTCRay` for rays packets of size `N=1`, and
+the `RTCRayNt` layout for ray packets of size not equal 1. The helper
+functions above to access a ray packet of size `N` take care of this
+incompatibility.
+
+Some callback functions get passed a hit structure with the following
+data members:
+
+  Member  In/Out  Description
+  ------- ------- ----------------------------------------------------------
+  instID  in      instance ID of hit instance
+  geomID  in      geometry ID of hit geometry
+  primID  in      primitive ID of hit primitive
+  u       in      barycentric u-coordinate of hit
+  v       in      barycentric v-coordinate of hit
+  t       in      hit distance
+  Ng      in      unnormalized geometry normal
+  ------- ------- ----------------------------------------------------------
+  : Data fields of a hit.
+
+This structure is in struct of array layout (SOA) for hit packets of
+size `N`. The layout of a hit packet of size `N` is defined by the
+`RTCHitNt` template in `embree2/rtcore_ray.h`.
+
+When the hit packet size is not known at compile time (e.g. when
+Embree returns a hit packet in the `RTCFilterFuncN` callback
+function), you can use the helper functions defined in
+`embree2/rtcore_ray.h` to access hit packet components:
+
+    unsigned& RTCHitN_instID(RTCHitN* hits, size_t N, size_t i);
+    unsigned& RTCHitN_geomID(RTCHitN* hits, size_t N, size_t i);
+    unsigned& RTCHitN_primID(RTCHitN* hits, size_t N, size_t i);
+
+    float& RTCHitN_u   (RTCHitN* hits, size_t N, size_t i);
+    float& RTCHitN_v   (RTCHitN* hits, size_t N, size_t i);
+    float& RTCHitN_t   (RTCHitN* hits, size_t N, size_t i);
+
+    float& RTCHitN_Ng_x(RTCHitN* hits, size_t N, size_t i);
+    float& RTCHitN_Ng_y(RTCHitN* hits, size_t N, size_t i);
+    float& RTCHitN_Ng_z(RTCHitN* hits, size_t N, size_t i);
+
+These helper functions get a pointer to the hit packet (`hits`
+parameter), the packet size `N`, and returns a reference to some
+component (e.g. u-component) of the the ith hit of the packet.
+
+
+Ray Queries
+-----------
+
+The API supports finding the closest hit of a ray segment with the
+scene (`rtcIntersect` functions), and determining if any hit between a
+ray segment and the scene exists (`rtcOccluded` functions).
+
+### Normal Mode
+
+In normal mode the following API functions should be used to trace rays:
+
+    void rtcIntersect  (                   RTCScene scene, RTCRay&    ray);
+    void rtcIntersect4 (const void* valid, RTCScene scene, RTCRay4&   ray);
+    void rtcIntersect8 (const void* valid, RTCScene scene, RTCRay8&   ray);
+    void rtcIntersect16(const void* valid, RTCScene scene, RTCRay16&  ray);
+    void rtcOccluded   (                   RTCScene scene, RTCRay&    ray);
+    void rtcOccluded4  (const void* valid, RTCScene scene, RTCRay4&   ray);
+    void rtcOccluded8  (const void* valid, RTCScene scene, RTCRay8&   ray);
+    void rtcOccluded16 (const void* valid, RTCScene scene, RTCRay16&  ray);
+
+The `rtcIntersect` and `rtcOccluded` function operate on single
+rays. The `rtcIntersect4` and `rtcOccluded4` functions operate on ray
+packets of size 4. The `rtcIntersect8` and `rtcOccluded8` functions
+operate on ray packets of size 8, and the `rtcIntersect16` and
+`rtcOccluded16` functions operate on ray packets of size 16.
+
+For the ray packet mode with packet size of 4, 8, or 16, the user has
+to provide a pointer to 4, 8, or 16 of 32 bit integers that act as a
+ray activity mask (`valid` argument). If one of these integers is set
+to `0x00000000` the corresponding ray is considered inactive and if
+the integer is set to `0xFFFFFFFF`, the ray is considered active. Rays
+that are inactive will not update any hit information.
+
+Finding the closest hit distance is done through the `rtcIntersect`
+type functions. These get the activity mask (`valid` parameter), the
+scene (`scene` parameter), and a ray as input (`ray` parameter). The
+layout of the ray structure is described in Section [Ray Layout]. The
+user has to initialize the ray origin (`org`), ray direction (`dir`),
+and ray segment (`tnear`, `tfar`). The ray segment has to be in the
+range $[0, ∞]$, thus ranges that start behind the ray origin are not
+valid, but ranges can reach to infinity. The geometry ID (`geomID`
+member) has to get initialized to `RTC_INVALID_GEOMETRY_ID` (-1). If
+the scene contains instances, also the instance ID (`instID`) has to
+get initialized to `RTC_INVALID_GEOMETRY_ID` (-1). If the scene
+contains motion blur geometries, also the ray time (`time`) has to get
+initialized to a value in the range $[0, 1]$.  If ray masks are
+enabled at compile time, also the ray mask (`mask`) has to get
+initialized. After tracing the ray, the hit distance (`tfar`),
+geometry normal (`Ng`), local hit coordinates (`u`, `v`), geometry ID
+(`geomID`), and primitive ID (`primID`) are set. If the scene contains
+instances, also the instance ID (`instID`) is set, if an instance is
+hit. The geometry ID corresponds to the ID returned at creation time
+of the hit geometry, and the primitive ID corresponds to the $n$th
+primitive of that geometry, e.g.  $n$th triangle. The instance ID
+corresponds to the ID returned at creation time of the instance.
+
+Testing if any geometry intersects with the ray segment is done through
+the `rtcOccluded` functions. Initialization has to be done as for
+`rtcIntersect`. If some geometry got found along the ray segment, the
+geometry ID (`geomID`) will get set to 0. Other hit information of the
+ray is undefined after calling `rtcOccluded`.
+
+In normal mode, data alignment requirements for ray query functions
+operating on single rays is 16 bytes for the ray. Data alignment
+requirements for query functions operating on AOS packets of 4, 8, or
+16 rays, is 16, 32, and 64 bytes respectively, for the valid mask and
+the ray. To operate on packets of 4 rays, the CPU has to support SSE,
+to operate on packets of 8 rays, the CPU has to support AVX, and
+to operate on packets of 16 rays, the CPU has to support AVX-512
+instructions. Additionally, the required ISA has to be enabled in
+Embree at compile time to use the desired packet size.
+
+The following code shows an example of setting up a single ray and
+traces it through the scene:
+
+    RTCRay ray;
+    ray.org = ray_origin;
+    ray.dir = ray_direction;
+    ray.tnear = 0.0f;
+    ray.tfar = inf;
+    ray.instID = RTC_INVALID_GEOMETRY_ID;
+    ray.geomID = RTC_INVALID_GEOMETRY_ID;
+    ray.primID = RTC_INVALID_GEOMETRY_ID;
+    ray.mask = 0xFFFFFFFF;
+    ray.time = 0.0f;
+    rtcIntersect(scene, ray);
+
+See tutorial [Triangle Geometry] for a complete example of how to
+trace rays.
+
+### Ray Stream Mode
+
+For the stream mode new functions got introduced that operate on
+streams of rays:
+
+    void rtcIntersect1M    (RTCScene scene, const RTCIntersectContext* context,
+                            RTCRay* rays, size_t M, size_t stride);
+    void rtcIntersect1Mp   (RTCScene scene, const RTCIntersectContext* context,
+                            RTCRay**rays, size_t M);
+    void rtcIntersectNM    (RTCScene scene, const RTCIntersectContext* context,
+                            RTCRayN* rays, size_t N, size_t M, size_t stride);
+    void rtcIntersectNp    (RTCScene scene, const RTCIntersectContext* context,
+                            RTCRayNp& rays, size_t N);
+
+    void rtcOccluded1M     (RTCScene scene, const RTCIntersectContext* context,
+                            RTCRay* rays, size_t M, size_t stride);
+    void rtcOccluded1Mp    (RTCScene scene, const RTCIntersectContext* context,
+                            RTCRay** rays, size_t M);
+    void rtcOccludedNM     (RTCScene scene, const RTCIntersectContext* context,
+                            RTCRayN* rays, size_t N, size_t M, size_t stride);
+    void rtcOccludedNp     (RTCScene scene, const RTCIntersectContext* context,
+                            RTCRayNp& rays, size_t N, size_t flags);
+
+The `rtcIntersectNM` and `rtcOccludedNM` ray stream functions operate
+on an array of `M` ray packets of packet size `N`. The offset in bytes
+between consecutive ray packets can be specified by the `stride`
+parameter. Data alignment requirements for ray streams is 16
+bytes. The packet size `N` has to be larger than 0 and the stream size
+`M` can be an arbitrary positive integer including 0. Tracing for
+example a ray stream consisting of four 8-wide SOA ray packets just
+requires to set the parameters `N` to 8, `M` to 4 and the `stride` to
+`sizeof(RTCRay8)`. A ray in a ray stream is considered inactive during
+traversal/intersection if its `tnear` value is larger than its `tfar`
+value.
+
+The ray streams functions `rtcIntersect1M` and `rtcOccluded1M` are
+just a shortcut for single ray streams with a packet size of
+`N=1`. `rtcIntersect1Mp` and `rtcOccluded1Mp` are similar to
+`rtcIntersect1M` and `rtcOccluded1M` while taking a stream of pointers
+to single rays as input. The `rtcIntersectNp` and `rtcOccludedNp`
+functions do not require the individual components of the SOA ray
+packets to be stored sequentially in memory, but at different adresses
+as specified in the `RTCRayNp` structure.
+
+The intersection context passed to the stream version of the ray query
+functions, can specify some intersection flags to optimize traversal
+and a `userRayExt` pointer that can be used to extent the ray with
+additional data as described in Section
+[Extending the Ray Structure]. The intersection context is propagated
+to each stream user callback function invoked.
+
+    struct RTCIntersectContext
+    {
+      RTCIntersectFlags flags;   //!< intersection flags
+      void* userRayExt;          //!< can be used to pass extended ray data to callbacks
+    };
+
+As intersection flag the user can currently specify if Embree should
+optimize traversal for coherent or incoherent ray distributions.
+
+    enum RTCIntersectFlags
+    {
+      RTC_INTERSECT_COHERENT   = 0,  //!< optimize for coherent rays
+      RTC_INTERSECT_INCOHERENT = 1   //!< optimize for incoherent rays
+    };
+
+The following code shows an example of setting up a stream of single
+rays and tracing it through the scene:
+
+    RTCRay rays[128];
+
+    /* first setup all rays */
+    for (size_t i=0; i<128; i++)
+    {
+      rays[i].org = calculate_ray_org(i);
+      rays[i].dir = calculate_ray_dir(i);
+      rays[i].tnear = 0.0f;
+      rays[i].tfar = inf;
+      rays[i].instID = RTC_INVALID_GEOMETRY_ID;
+      rays[i].geomID = RTC_INVALID_GEOMETRY_ID;
+      rays[i].primID = RTC_INVALID_GEOMETRY_ID;
+      rays[i].mask = 0xFFFFFFFF;
+      rays[i].time = 0.0f;
+    }
+
+    /* now create a context and trace the ray stream */
+    RTCIntersectContext context;
+    context.flags = RTC_INTERSECT_INCOHERENT;
+    context.userRayExt = nullptr;
+    rtcIntersectNM(scene, &context, &rays, 1, 128, sizeof(RTCRay));
+
+See tutorial [Stream Viewer] for a complete example of how to
+trace ray streams.
+
+
+Interpolation of Vertex Data
+----------------------------
+
+Smooth interpolation of per-vertex data is supported for triangle
+meshes, quad meshs, hair geometry, line segment geometry, and
+subdivision geometry using the `rtcInterpolate2` API call. This
+interpolation function does ignore displacements and always
+interpolates the underlying base surface.
+
+    void rtcInterpolate2(RTCScene scene,
+                         unsigned geomID, unsigned primID,
+                         float u, float v,
+                         RTCBufferType buffer,
+                         float* P,
+                         float* dPdu, float* dPdv,
+                         float* ddPdudu, float* ddPdvdv, float* ddPdudv,
+                         size_t numFloats);
+
+This call smoothly interpolates the per-vertex data stored in the
+specified geometry buffer (`buffer` parameter) to the u/v location
+(`u` and `v` parameters) of the primitive (`primID` parameter) of the
+geometry (`geomID` parameter) of the specified scene (`scene`
+parameter). The interpolation buffer (`buffer` parameter) has to
+contain (at least) `numFloats` floating point values per vertex to
+interpolate. As interpolation buffer one can specify the
+`RTC_VERTEX_BUFFER0` and `RTC_VERTEX_BUFFER1` as well as one of two
+special user vertex buffers `RTC_USER_VERTEX_BUFFER0` and
+`RTC_USER_VERTEX_BUFFER1`. These user vertex buffers can only get set
+using the `rtcSetBuffer2` call, they cannot get managed internally by
+Embree as they have no default layout. The last element of the buffer
+has to be padded to 16 bytes, such that it can be read safely using
+SSE instructions.
+
+The `rtcInterpolate` call stores `numFloats` interpolated floating
+point values to the memory location pointed to by `P`. One can avoid
+storing the interpolated value by setting `P` to NULL.
+
+The first order derivative of the interpolation by u and v are stored
+at the `dPdu` and `dPdv` memory locations. One can avoid storing first
+order derivatives by setting both `dPdu` and `dPdv` to NULL.
+
+The second order derivatives are stored at the `ddPdudu`, `ddPdvdv`,
+and `ddPdudv` memory locations. One can avoid storing second order
+derivatives by setting these three pointers to NULL.
+
+The `RTC_INTERPOLATE` algorithm flag of a scene has to be enabled to
+perform interpolations.
+
+It is explicitly allowed to call this function on disabled
+geometries. This makes it possible to use a separate subdivision mesh
+with different vertex creases, edge creases, and boundary handling for
+interpolation of texture coordinates if that is necessary.
+
+The applied interpolation will do linear interpolation for triangle
+and quad meshes, linear interpolation for line segments, cubic Bézier
+interpolation for hair, and apply the full subdivision rules for
+subdivision geometry.
+
+There is also a second interpolate call `rtcInterpolateN2` that can be
+used for ray packets.
+
+    void rtcInterpolateN2(RTCScene scene, unsigned geomID,
+                          const void* valid, const unsigned* primIDs,
+                          const float* u, const float* v, size_t numUVs,
+                          RTCBufferType buffer,
+                          float* dP,
+                          float* dPdu, float* dPdv,
+                          float* ddPdudu, float* ddPdvdv, float* ddPdudv,
+                          size_t numFloats);
+
+This call is similar to the first version, but gets passed `numUVs`
+many u/v coordinates and a valid mask (`valid` parameter) that
+specifies which of these coordinates are valid. The valid mask points
+to `numUVs` integers and a value of -1 denotes valid and 0 invalid. If
+the valid pointer is NULL all elements are considers valid. The
+destination arrays are filled in structure of array (SoA) layout.
+
+See tutorial [Interpolation] for an example of using the
+`rtcInterpolate2` function.
+
+Buffer Sharing
+--------------
+
+Embree supports sharing of buffers with the application. Each buffer
+that can be mapped for a specific geometry can also be shared with the
+application, by passing a pointer, offset, stride, and number of
+elements of the application side buffer using the `rtcSetBuffer2` API
+function.
+
+    void rtcSetBuffer2(RTCScene scene, unsigned geomID, RTCBufferType type,
+                      void* ptr, size_t offset, size_t stride, size_t size);
+
+The `rtcSetBuffer2` function has to get called before any call to
+`rtcMapBuffer` for that buffer, otherwise the buffer will get allocated
+internally and the call to `rtcSetBuffer2` will fail. The buffer has to
+remain valid as long as the geometry exists, and the user is responsible
+to free the buffer when the geometry gets deleted. When a buffer is
+shared, it is safe to modify that buffer without mapping and unmapping
+it. However, for dynamic scenes one still has to call `rtcUpdate` for
+modified geometries and the buffer data has to stay constant from the
+`rtcCommit` call to after the last ray query invocation.
+
+The `offset` parameter specifies a byte offset to the start of the
+first element, the `stride` parameter specifies a byte stride between
+the different elements of the shared buffer and the `size` parameter
+specified the number of elements stored inside the buffer. This
+support for offset and stride allows the application quite some
+freedom in the data layout of these buffers, however, some
+restrictions apply. Index buffers always store 32 bit indices and
+vertex buffers always store single precision floating point data. The
+start address `ptr+offset` and `stride` always have to be aligned to 4
+bytes, otherwise the `rtcSetBuffer2` function will fail. The `size`
+parameter can be used to change the size of a buffer, which makes it
+possible to change the number of elements inside a mesh (by changing
+the size of the `RTC_INDEX_BUFFER`).
+
+For vertex buffers (`RTC_VERTEX_BUFFER` and `RTC_USER_VERTEX_BUFFER`),
+the last element must be readable using SSE instructions, thus padding
+the last element to 16 bytes size is required for some layouts.
+
+The following is an example of how to create a mesh with shared index
+and vertex buffers:
+
+    unsigned geomID = rtcNewTriangleMesh(scene, geomFlags, numTriangles, numVertices);
+    rtcSetBuffer2(scene, geomID, RTC_VERTEX_BUFFER, vertexPtr, 0, 3*sizeof(float), numVertices);
+    rtcSetBuffer2(scene, geomID, RTC_INDEX_BUFFER, indexPtr, 0, 3*sizeof(int), numTriangles);
+
+Sharing buffers can significantly reduce the memory required by the
+application, thus we recommend using this feature. When enabling the
+`RTC_COMPACT` scene flag, the spatial index structures of Embree might
+also share the vertex buffer, resulting in even higher memory savings.
+
+Multi-Segment Motion Blur
+-------------------------
+
+All geometry types support multi-segment motion blur with equidistant
+time steps and arbitrary number of time steps in the range of 2
+to 129. Each geometry can have a different number of time steps. Some
+motion blur geometry is constructed by passing the number of time
+steps to the geometry construction function and setting the
+vertex arrays `RTC_VERTEX_BUFFER0+t` for each time step `t`:
+
+    unsigned geomID = rtcNewTriangleMesh(scene, geomFlags, numTris, numVertices, 3);
+    rtcSetBuffer2(scene, geomID, RTC_VERTEX_BUFFER0+0, vertex0Ptr, 0, sizeof(Vertex), numVertices);
+    rtcSetBuffer2(scene, geomID, RTC_VERTEX_BUFFER0+1, vertex1Ptr, 0, sizeof(Vertex), numVertices);
+    rtcSetBuffer2(scene, geomID, RTC_VERTEX_BUFFER0+2, vertex2Ptr, 0, sizeof(Vertex), numVertices);
+    rtcSetBuffer2(scene, geomID, RTC_INDEX_BUFFER, indexPtr, 0, sizeof(Triangle), numTris);
+
+If a scene contains geometries with motion blur, the user has to set
+the `time` member of the ray to a value in the range $[0, 1]$. The
+motion blur geometry is defined by linearly interpolating the
+geometries of neighboring time steps. Each ray can specify a different
+time, even inside a ray packet.
+
+User Data Pointer
+-----------------
+
+A user data pointer can be specified and queried per geometry, to
+efficiently map from the geometry ID returned by ray queries to the
+application representation for that geometry.
+
+    void  rtcSetUserData (RTCScene scene, unsigned geomID, void* ptr);
+    void* rtcGetUserData (RTCScene scene, unsigned geomID);
+
+The user data pointer of some user defined geometry get additionally
+passed to the intersect and occluded callback functions of that user
+geometry. Further, the user data pointer is also passed to
+intersection filter callback functions attached to some geometry.
+
+The `rtcGetUserData` function is on purpose not thread safe with
+respect to other API calls that modify the scene. Consequently, this
+function can be used to efficiently query the user data pointer during
+rendering (also by multiple threads), but should not get called
+while modifying the scene with other threads.
+
+Geometry Mask
+-------------
+
+A 32 bit geometry mask can be assigned to triangle meshes and hair
+geometries using the `rtcSetMask` call.
+
+    rtcSetMask(scene, geomID, mask);
+
+Only if the bitwise `and` operation of this mask with the mask stored
+inside the ray is not 0, primitives of this geometry are hit by a ray.
+This feature can be used to disable selected triangle mesh or hair
+geometries for specifically tagged rays, e.g. to disable shadow casting
+for some geometry. This API feature is disabled in Embree by default at
+compile time, and can be enabled in CMake through the
+`EMBREE_RAY_MASK` parameter.
+
+Filter Functions
+----------------
+
+The API supports per geometry filter callback functions that are invoked
+for each intersection found during the `rtcIntersect` or `rtcOccluded`
+calls. The former ones are called intersection filter functions, the
+latter ones occlusion filter functions. The filter functions can be used
+to implement various useful features, such as accumulating opacity for
+transparent shadows, counting the number of surfaces along a ray,
+collecting all hits along a ray, etc. Filter functions can also be used
+to selectively reject hits to enable backface culling for some
+geometries. If the backfaces should be culled in general for all
+geometries then it is faster to enable `EMBREE_BACKFACE_CULLING` during
+compilation of Embree instead of using filter functions.
+
+If the `RTC_SCENE_HIGH_QUALITY` mode is set, the intersection and
+occlusion filter functions may be called multiple times for the same
+hit. For some usage scenarios, the application may have to work around
+this by collecting already reported hits (geomID/primID pairs) and
+ignoring duplicates for some usage scenarios.
+
+### Normal Mode
+
+In normal mode the filter functions provided by the user need to have
+the following signature:
+
+    void RTCFilterFunc  (                   void* userDataPtr, RTCRay&   ray);
+    void RTCFilterFunc4 (const void* valid, void* userDataPtr, RTCRay4&  ray);
+    void RTCFilterFunc8 (const void* valid, void* userDataPtr, RTCRay8&  ray);
+    void RTCFilterFunc16(const void* valid, void* userDataPtr, RTCRay16& ray);
+
+The `valid` pointer points to an integer valid mask (0 means invalid
+and -1 means valid). The `userDataPtr` is a user pointer optionally
+set per geometry through the `rtcSetUserData` function. All hit
+information inside the ray is valid. If the hit geometry is instanced,
+the `instID` member of the ray is valid and the ray origin, direction,
+and geometry normal visible through the ray are in object space.
+
+The filter function can reject a hit by setting the `geomID` member of
+the ray to `RTC_INVALID_GEOMETRY_ID`, otherwise the hit is
+accepted. The filter function is not allowed to modify the ray input
+data (`org`, `dir`, `time`, `mask`, and `tnear` members), but can
+modify the hit data of the ray (`u`, `v`, `Ng`, `tfar`, `geomID`,
+`primID`, and `instID` members). Updating the `tfar` distance to a
+smaller value is possible without limitation. However, increasing the
+`tfar` distance of the ray to a larger value `tfar'` , does not
+guarantee intersections between `tfar` and `tfar'` to be reported
+later, as the corresponding subtrees might have gotten culled already.
+
+The intersection and occlusion filter functions for different ray
+types are set for some geometry of a scene using the following API
+functions:
+
+    void rtcSetIntersectionFilterFunction  (RTCScene, unsigned geomID, RTCFilterFunc   filter);
+    void rtcSetIntersectionFilterFunction4 (RTCScene, unsigned geomID, RTCFilterFunc4  filter);
+    void rtcSetIntersectionFilterFunction8 (RTCScene, unsigned geomID, RTCFilterFunc8  filter);
+    void rtcSetIntersectionFilterFunction16(RTCScene, unsigned geomID, RTCFilterFunc16 filter);
+
+    void rtcSetOcclusionFilterFunction  (RTCScene, unsigned geomID, RTCFilterFunc   filter);
+    void rtcSetOcclusionFilterFunction4 (RTCScene, unsigned geomID, RTCFilterFunc4  filter);
+    void rtcSetOcclusionFilterFunction8 (RTCScene, unsigned geomID, RTCFilterFunc8  filter);
+    void rtcSetOcclusionFilterFunction16(RTCScene, unsigned geomID, RTCFilterFunc16 filter);
+
+The intersection and occlusion filter functions of type `RTCFilterFunc`
+are only called by the `rtcIntersect` and `rtcOccluded`
+functions. Similar the filter functions of type `FilterFunc4`,
+`FilterFunc8`, and `FilterFunc16` are called by `rtcIntersect4/8/16`
+and `rtcOccluded4/8/16` of matching width.
+
+### Stream Mode
+
+For ray stream mode a new type of filter function `RTCFilterFuncN` got
+introduced:
+
+    void RTCFilterFuncN (int* valid,
+                         void* userDataPtr,
+                         const RTCIntersectContext* context,
+                         RTCRayN* ray,
+                         const RTCHitN* potentialHit,
+                         const size_t N);
+
+The stream intersection and occlusion filter functions of this new
+type are set for some geometry of a scene using the following API
+functions:
+
+    void rtcSetIntersectionFilterFunctionN (RTCScene, unsigned geomID, RTCFilterFuncN filter);
+    void rtcSetOcclusionFilterFunctionN    (RTCScene, unsigned geomID, RTCFilterFuncN filter);
+
+For the callback `RTCFilterFuncN`, the `valid` parameter points to an
+integer valid mask (0 means invalid and -1 means valid). The
+`userDataPtr` is a user pointer optionally set per geometry through
+the `rtcSetUserData` function. The `context` parameter points to the
+intersection context passed to the ray query function. The `ray`
+parameter contains the current ray. All hit data inside the `ray` are
+undefined, except the `tfar` value. The `potentialHit` parameter
+points to the new hit to test and update. The `N` parameter is the
+number of rays and hits found in the `ray` and `potentialHit`. If the
+hit geometry is instanced, the `instID` member of the ray is valid and
+the ray as well as the potential hit are in object space.
+
+As the ray packet size `N` can be arbitrary, the ray and hit should
+get accessed through the helper functions as describe in Section
+[Ray Layout].
+
+The callback function has the task to check for each valid ray whether
+it wants to accept or reject the corresponding hit. To reject a hit,
+the filter callback function just has to write `0` to the integer
+valid mask of the corresponding ray. The filter function is not
+allowed to modify the ray input data (`org`, `dir`, `time`, `mask`,
+and `tnear` members), nor the potential hit, nor inactive components.
+
+An intersection filter callback function can accept a hit by updating
+all hit data members of the ray (`u`, `v`, `Ng`, `tfar`, `geomID`,
+`primID`, and `instID` members) and keep the valid mask set to `-1`.
+
+An occlusion filter callback function can accept a hit by setting the
+`geomID` member of the ray to `0` and keep the valid mask set to `-1`.
+
+The intersection filter callback of most applications will just copy
+the `potentialHit` into the appropiate fields of the ray, but this is
+not a requirement and the hit data of the ray can get modified
+arbitrarily. Updating the `tfar` distance to a smaller value (e.g. the
+`t` distance of the potential hit) is possible without
+limitation. However, increasing the `tfar` distance of the ray to a
+larger value `tfar'` , does not guarantee intersections between `tfar`
+and `tfar'` to be reported later, as the corresponding subtrees might
+have gotten culled already.
+
+Displacement Mapping Functions
+------------------------------
+
+The API supports displacement mapping for subdivision meshes. A
+displacement function can be set for some subdivision mesh using the
+`rtcSetDisplacementFunction` API call.
+
+    void rtcSetDisplacementFunction2(RTCScene, unsigned geomID, RTCDisplacementFunc, RTCBounds*);
+
+A displacement function of `NULL` will delete an already set
+displacement function. The bounds parameter is optional. If `NULL` is
+passed as bounds, then the displacement shader will get evaluated
+during the build process to properly bound displaced geometry. If a
+pointer to some bounds of the displacement are passed, then the
+implementation can choose to use these bounds to bound displaced
+geometry. When bounds are specified, then these bounds have to be
+conservative and should be tight for best performance.
+
+The displacement function has to have the following type:
+
+    typedef void (*RTCDisplacementFunc2)(void* ptr,
+                                         unsigned geomID, unsigned primID, unsigned timeStep,
+                                         const float* u,  const float* v,
+                                         const float* nx, const float* ny, const float* nz,
+                                         float* px, float* py, float* pz,
+                                         size_t N);
+
+The displacement function is called with the user data pointer of the
+geometry (`ptr`), the geometry ID (`geomID`), and primitive ID
+(`primID`) of a patch to displace. For motion blur the time step
+`timeStep` is also specified, such that the function can be time
+varying. For the patch, a number N of points to displace are
+specified in a struct of array layout. For each point to displace the
+local patch UV coordinates (`u` and `v` arrays), the normalized
+geometry normal (`nx`, `ny`, and `nz` arrays), as well as world space
+position (`px`, `py`, and `pz` arrays) are provided. The task of the
+displacement function is to use this information and move the world
+space position inside the allowed specified bounds around the point.
+
+All passed arrays are guaranteed to be 64 bytes aligned, and properly
+padded to make wide vector processing inside the displacement function
+possible.
+
+The displacement mapping functions might get called during the
+`rtcCommit` call, or lazily during the `rtcIntersect` or
+`rtcOccluded` calls.
+
+Also see tutorial [Displacement Geometry] for an example of how to use
+the displacement mapping functions.
+
+Extending the Ray Structure
+---------------------------
+
+### Normal Mode
+
+If Embree is used in normal mode, the ray passed to the filter
+callback functions and user geometry callback functions is guaranteed
+to be the same ray pointer initially provided to the ray query
+function by the user. For that reason, it is safe to extend the ray by
+additional data and access this data inside the filter callback
+functions (e.g. to accumulate opacity) and user geometry callback
+functions.
+
+### Stream Mode
+
+If Embree is used in stream mode, the ray passed to the filter
+callback and user geometry callback functions is `not` guaranteed to
+be the same ray pointer initially passed to the ray query function, as
+the stream implementation may decide to copy rays around, reorder
+them, and change the data layout internally when appropiate (e.g. SOA
+to AOS conversion).
+
+To identify specific rays in the callback functions, the user has to
+pass an ID with each ray and set the `userRayExt` member of the
+intersection context to point to its ray extensions. The ray
+extensions can be stored in a seprarate memory location but also just
+after the end of each ordinary ray (or ray packet). In the latter
+case, you can just point the `userRayExt` to the input rays.
+
+To encode a ray ID the ray mask field can be used entirely when the
+ray mask feature is disabled, or unused bits of the ray mask can be
+used in case the ray mask feature is enabled (e.g. by using the lower
+16 bits as ray ID, and the upper 16 bits as ray mask, and setting the
+lower 16 bits of each geometry mask always to 0).
+
+The intersection context provided to the stream ray query functions is
+passed to each stream callback function (e.g. `RTCIntersectFuncN`,
+`RTCIntersectFunc1Mp`, or `RTCFilterFuncN`). Thus, in the callback
+function, the ray ID can get decoded, and the extended ray data
+accessed through the `userRayExt` pointer stored inside the
+intersection context. For SPMD type programs this access requires
+`gather` and `scatter` operations to access the user ray extensions.
+
+Not that using the ray ID to access the ray extensions is necessary,
+as the ray IDs might have changed from the IDs passed to the ray query
+function. E.g. if you trace a ray packet with 8 rays 0 to 8, then even
+if a callback gets called with a ray packet of 8 rays, they rays might
+have gotten reordered. Further, the callback might get called with a
+subpacket of a size smaller than 8 (e.g. `N=5`). However, optimizing
+for the common case in which Embree keeps such a packet intact (thus
+having a special codepath for `N=8` and unchanged IDs) can give higher
+performance.
+
+
+Sharing Threads with Embree
+---------------------------
+
+On some implementations, Embree supports using the application threads
+when building internal data structures, by using the
+
+    void rtcCommitThread(RTCScene, unsigned threadIndex, unsigned threadCount);
+
+API call to commit the scene. This function has to get called by all
+threads that want to cooperate in the scene commit. Each call is
+provided the scene to commit, the index of the calling thread in the
+range [0, `threadCount`-1], and the number of threads that will call
+into this commit operation for the scene. All threads will return
+again from this function after the scene commit is finished.
+
+Multiple such scene commit operations can also be running at the same
+time, e.g. it is possible to commit many small scenes in parallel
+using one thread per commit operation. Subsequent commit operations
+for the same scene can use different number of threads in the
+`rtcCommitThread` or use the Embree internal threads using the
+`rtcCommit` call.
+
+*Note:* When using Embree with the Intel® Threading Building Blocks
+(which is the default) you should not use the `rtcCommitThread`
+function. Sharing of your threads with TBB is not possible and TBB
+will always generate its own set of threads. We recommend to also use
+TBB inside your application to share threads with the Embree
+library. When using TBB inside your application do never use the
+`rtcCommitThread` function.
+
+*Note:* When enabling the Embree internal tasking system the
+`rtcCommitThread` feature will work as expected and use the
+application threads for hierarchy building.
+
+Join Build Operation
+--------------------
+
+The special `rtcCommitJoin` function can be used from multiple threads
+to join a scene build operation. All thread have to consistently call
+`rtcCommitJoin` and no other commit variant.
+
+This feature allows a flexible way to lazily create hierarchies during
+rendering. A thread reaching a not yet constructed sub-scene of a
+two-level scene, can generate the sub-scene geometry and call
+`rtcCommitJoin` on that just generated scene. During construction,
+further threads reaching the not-yet-built scene, can join the build
+operation by also invoking `rtcCommitJoin`. A thread that calls
+`rtcCommitJoin` after the build finishes, will directly return from
+the `rtcCommitJoin` call (even for static scenes).
+
+*Note:* When using Embree with the Intel® Threading Building Blocks,
+thread that call `rtcCommitJoin` will join the build operation, but
+other TBB worker threads might also participate in the build. To avoid
+thread oversubscription, we recommend using TBB also inside the
+application. Further, the join mode only works properly starting with
+TBB v4.4 Update 1. For earlier TBB versions threads that call
+`rtcCommitJoin` to join a running build will just wait for the build
+to finish.
+
+*Note:* When using Embree with the internal tasking system,
+exclusively threads that call `rtcCommitJoin` will perform the build
+operation, and no additional worker threads are scheduled.
+
+Memory Monitor Callback
+---------------------------
+
+Using the memory monitor callback mechanism, the application can track
+the memory consumption of an Embree device, and optionally terminate
+API calls that consume too much memory.
+
+The user provided memory monitor callback function must have the
+following signature:
+
+    bool (*RTCMemoryMonitorFunc2)(void* userPtr, const ssize_t bytes, const bool post);
+
+A single such callback function per device can be registered by calling
+
+    rtcDeviceSetMemoryMonitorFunction2(RTCDevice device, RTCMemoryMonitorFunc2 func, void* userPtr);
+
+and deregistered again by calling it with `NULL` as function
+pointer. Once registered the Embree device will invoke the callback
+function before or after it allocates or frees important memory
+blocks. The `userPtr` value that is set at registration time is
+passed to each invokation of the callback function. The callback
+function might get called from multiple threads concurrently.
+
+The application can track the current memory usage of the Embree
+device by atomically accumulating the provided `bytes` input
+parameter. This parameter will be >0 for allocations and <0 for
+deallocations. The `post` input parameter is true if the callback
+function was invoked after the allocation or deallocation, otherwise
+it is false.
+
+Embree will continue its operation normally when returning true from
+the callback function. If false is returned, Embree will cancel the
+current operation with the RTC_OUT_OF_MEMORY error code. Cancelling
+will only happen when the callback was called for allocations (bytes >
+0), otherwise the cancel request will be ignored. If a callback that
+was invoked before the allocation happens (`post == false`) cancels
+the operation, then the `bytes` parameter should not get accumulated,
+as the allocation will never happen. If a callback that was called
+after the allocation happened (`post == true`) cancels the operation,
+then the `bytes` parameter should get accumulated, as the allocation
+properly happened. Issuing multiple cancel requests for the same
+operation is allowed.
+
+Progress Monitor Callback
+---------------------------
+
+The progress monitor callback mechanism can be used to report progress
+of hierarchy build operations and to cancel long lasting build
+operations.
+
+The user provided progress monitor callback function has to have the
+following signature:
+
+    bool (*RTCProgressMonitorFunc)(void* userPtr, const double n);
+
+A single such callback function can be registered per scene by
+calling
+
+    rtcSetProgressMonitorFunction(RTCScene, RTCProgressMonitorFunc, void* userPtr);
+
+and deregistered again by calling it with `NULL` for the callback
+function. Once registered Embree will invoke the callback function
+multiple times during hierarchy build operations of the scene, by
+providing the `userPtr` pointer that was set at registration time, and a
+double `n` in the range $[0, 1]$ estimating the completion amount of the
+operation. The callback function might get called from multiple threads
+concurrently.
+
+When returning `true` from the callback function, Embree will continue
+the build operation normally. When returning `false` Embree will
+cancel the build operation with the RTC_CANCELLED error code. Issuing
+multiple cancel requests for the same build operation is allowed.
+
+Configuring Embree
+------------------
+
+Some internal device parameters can be set and queried using the
+`rtcDeviceSetParameter1i` and `rtcDeviceGetParameter1i` API call. The
+parameters from the following table are available to set/query:
+
+  -------------------------------------- ------------------------------------- ------------
+  Parameter                              Description                           Read/Write
+  -------------------------------------- ------------------------------------- ------------
+  RTC_CONFIG_VERSION_MAJOR               returns Embree major version          Read only
+
+  RTC_CONFIG_VERSION_MINOR               returns Embree minor version          Read only
+
+  RTC_CONFIG_VERSION_PATCH               returns Embree patch version          Read only
+
+  RTC_CONFIG_VERSION                     returns Embree version as integer     Read only
+                                         e.g. Embree v2.8.2 → 20802
+
+  RTC_CONFIG_INTERSECT1                  checks if `rtcIntersect1` is          Read only
+                                         supported
+
+  RTC_CONFIG_INTERSECT4                  checks if `rtcIntersect4` is          Read only
+                                         supported
+
+  RTC_CONFIG_INTERSECT8                  checks if `rtcIntersect8` is          Read only
+                                         supported
+
+  RTC_CONFIG_INTERSECT16                 checks if `rtcIntersect16` is         Read only
+                                         supported
+
+  RTC_CONFIG_INTERSECT_STREAM            checks if `rtcIntersect1M`,
+                                         `rtcIntersect1Mp`, `rtcIntersectNM`,  Read only
+                                         and `rtcIntersectNp` are supported
+
+  RTC_CONFIG_TRIANGLE_GEOMETRY           checks if triangle geometries are     Read only
+                                         supported
+
+  RTC_CONFIG_QUAD_GEOMETRY               checks if quad geometries are         Read only
+                                         supported
+
+  RTC_CONFIG_LINE_GEOMETRY               checks if line geometries are         Read only
+                                         supported
+
+  RTC_CONFIG_HAIR_GEOMETRY               checks if hair geometries are         Read only
+                                         supported
+
+  RTC_CONFIG_SUBDIV_GEOMETRY             checks if subdivision meshes are      Read only
+                                         supported
+
+  RTC_CONFIG_USER_GEOMETRY               checks if user geometries are         Read only
+                                         supported
+
+  RTC_CONFIG_RAY_MASK                    checks if ray masks are supported     Read only
+
+  RTC_CONFIG_BACKFACE_CULLING            checks if backface culling is         Read only
+                                         supported
+
+  RTC_CONFIG_INTERSECTION_FILTER         checks if intersection filters are    Read only
+                                         enabled
+
+  RTC_CONFIG_INTERSECTION_FILTER_RESTORE checks if intersection filters        Read only
+                                         restore previous hit
+
+  RTC_CONFIG_IGNORE_INVALID_RAYS         checks if invalid rays are ignored    Read only
+
+  RTC_CONFIG_TASKING_SYSTEM              return used tasking system            Read only
+                                         (0 = INTERNAL, 1 = TBB)
+
+  RTC_SOFTWARE_CACHE_SIZE                Configures the software cache size    Write only
+                                         (used to cache subdivision surfaces
+                                         for instance). The size is specified
+                                         as an integer number of bytes. The
+                                         software cache cannot be configured
+                                         during rendering.
+
+  RTC_CONFIG_COMMIT_JOIN                 Checks if rtcCommit can be used to    Read only
+                                         join build operation (not supported
+                                         when Embree is compiled with some
+                                         older TBB versions)
+                                         
+  RTC_CONFIG_COMMIT_THREAD               Checks if rtcCommitThread is          Read only
+                                         available (not supported when
+                                         Embree is compiled with some older
+                                         TBB versions)
+
+  -------------------------------------- ------------------------------------- ------------
+  : Parameters for `rtcDeviceSetParameter` and `rtcDeviceGetParameter`.
+
+For example, to configure the size of the internal software
+cache that is used to handle subdivision surfaces use the
+`RTC_SOFTWARE_CACHE_SIZE` parameter to set desired size of the cache
+in bytes:
+
+    rtcDeviceSetParameter1i(device, RTC_SOFTWARE_CACHE_SIZE, bytes);
+
+The software cache cannot get configured while any Embree API call is
+executed. Best configure the size of the cache only once at
+application start.
+
+
+Limiting number of Build Threads
+--------------------------------
+
+You can use the TBB API to limit the number of threads used by Embree
+during hierarchy construction. Therefore just create a global
+taskscheduler_init object, initialized with the number of threads to
+use:
+
+    #include <tbb/tbb.h>
+
+    tbb::task_scheduler_init init(numThreads);
+
+
+Thread Creation and Affinity Settings
+--------------------------------------
+
+Tasking systems like TBB create worker threads on demand which will
+add a runtime overhead for the very first `rtcCommit` call. In case
+you want to benchmark the scene build time, you should start threads
+at application startup. You can let Embree start TBB threads by
+passing `start_threads=1` to the init parameter of `rtcNewDevice`.
+
+On machines with a high thread count (e.g. dual-socket Xeon or Xeon
+Phi machines), affinitizing TBB worker threads increases build and
+rendering performance. You can let Embree affinitize TBB worker
+threads by passing `set_affinity=1` to the init parameter of
+`rtcNewDevice`.
+
+All Embree tutorials automatically start and affinitize TBB worker threads
+by passing `start_threads=1,set_affinity=1` to `rtcNewDevice`.
+
+
+Huge Page Support
+--------------------------------
+
+We recommend using 2MB huge pages with Embree as this improves ray
+tracing performance by about 10%. Huge pages are currently only
+working under Linux with Embree.
+
+To enable transparent huge page support under Linux execute the
+following as root:
+
+    echo always > /sys/kernel/mm/transparent_hugepage/enabled
+
+When transparent huge pages are enabled, the kernel tries to merge 4k
+pages to 2MB pages when possible as a background job. See the
+following webpage for more information on transparent huge pages under Linux
+[https://www.kernel.org/doc/Documentation/vm/transhuge.txt](https://www.kernel.org/doc/Documentation/vm/transhuge.txt).
+
+Using that first approach the transitioning from 4k to 2MB pages might
+take some time. For that reason Embree also supports allocating 2MB
+pages directly when a huge page pool is configured. To configure 2GB
+of adress space for huge page allocation, execute the following as root:
+
+    echo 1000 > /proc/sys/vm/nr_overcommit_hugepages
+
+See the following webpage for more information on huge pages under
+Linux [https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt](https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt).
+
+
+BVH Builder API
+--------------------------------
+
+The Embree API exposes internal BVH builders to build BVHs with any
+desired node and leaf layout. To invoke the BVH builder you have to
+create a BVH object using the `rtcNewBVH` function and deleted again
+using the `rtcDeleteBVH` function.
+
+    RTCBVH rtcNewBVH(RTCDevice device);
+    void rtcDeleteBVH(RTCBVH bvh);
+
+This BVH contains some builder state and fast node allocator. Some
+settings have to be passed to be BVH build function:
+
+    enum RTCBuildQuality
+    {
+      RTC_BUILD_QUALITY_LOW = 0,     //!< build low quality BVH (good for dynamic scenes)
+      RTC_BUILD_QUALITY_NORMAL = 1,  //!< build standard quality BVH
+      RTC_BUILD_QUALITY_HIGH = 2,    //!< build high quality BVH
+    };
+      
+    struct RTCBuildSettings
+    {
+      unsigned size;               //!< size of this structure in bytes
+      RTCBuildQuality quality;     //!< quality of BVH build
+      unsigned maxBranchingFactor; //!< branching factor of BVH to build
+      unsigned maxDepth;           //!< maximal depth of BVH to build
+      unsigned sahBlockSize;       //!< blocksize for SAH heuristic
+      unsigned minLeafSize;        //!< minimal size of a leaf
+      unsigned maxLeafSize;        //!< maximal size of a leaf
+      float travCost;              //!< estimated cost of one traversal step
+      float intCost;               //!< estimated cost of one primitive intersection
+      unsigned extraSpace;         //!< for spatial splitting we need extra space at end of primitive array
+    };
+
+Some default values for the settings can be obtained using the
+`rtcDefaultBuildSettings` function. Using the `quality` setting, one
+can select between a faster low quality build which is good for
+dynamic scenes, and a standard quality build for static scenes. One
+can also specify the desired maximal branching factor of the BVH
+(`maxBranchingFactor` setting), the maximal depth the BVH should have
+(`maxDepth` setting), some power of 2 block size for the SAH heuristic
+(`sahBlockSize`), the minimal and maximal leaf size (`minLeafSize` and
+`maxLeafSize` setting), and the estimated cost of one traversal step
+and primitve intersection (`travCost` and `intCost` setting). To
+spatially split primitives in high quality mode, the builder needs
+some extra space at the end of the build primitive array. The amount
+of extra space can be passed using the `extraSpace` setting, and
+should be about the same size as there are primitives. The `size`
+member has always to be set to the size of the `RTCBuildSettings`
+structure in bytes.
+
+Four callback functions have to get registered which are invoked
+during build to create BVH nodes (`RTCCreateNodeFunc`), set the
+pointers to all children (`RTCSetNodeChildrenFunc`), set the bounding
+boxes of all children (`RTCSetNodeBoundsFunc`), and to create a leaf
+node (`RTCCreateLeafFunc`).
+
+    typedef void* (*RTCCreateNodeFunc) (RTCThreadLocalAllocator allocator,
+                                        size_t numChildren, void* userPtr);
+
+    typedef void  (*RTCSetNodeChildFunc) (void* nodePtr, void** childPtrs, size_t numChildren,
+                                          void* userPtr);
+
+    typedef void  (*RTCSetNodeBoundsFunc) (void* nodePtr, const RTCBounds** bounds, size_t numChildren,
+                                           void* userPtr);
+
+    typedef void* (*RTCCreateLeafFunc) (RTCThreadLocalAllocator allocator,
+                                        const RTCBuildPrimitive* primitives, size_t numPrimitives,
+                                        void* userPtr);
+
+    typedef void  (*RTCSplitPrimitiveFunc) (const RTCBuildPrimitive& prim,
+        unsigned dim, float pos, RTCBounds& lbounds, RTCBounds& rbounds, void* userPtr);
+
+The `RTCCreateNodeFunc` and `RTCCreateLeafFunc` type callbacks are
+passed a thread local allocator object that should be used for fast
+allocation of nodes using the `rtcThreadLocalAlloc` function. 
+
+    void* rtcThreadLocalAlloc(RTCThreadLocalAllocator allocator, size_t bytes, size_t align);
+
+We strongly recommend using this allocation mechanism, as alternative
+approaches like standard `malloc` can be over 10x slower. The
+allocator object passed to the create callbacks has to be used only
+inside the current thread.
+
+The `RTCCreateNodeFunc` callback additionally gets passed the number
+of children for this node in the range from 2 to maxBranchingFactor
+(`numChildren` argument).
+
+The `RTCSetNodeChildFunc` callback function, gets passed a pointer to
+the node as input (`nodePtr` argument), an array of pointers to the
+children (`childPtrs` argument), and the size of this array
+(`numChildren` argument).
+
+The `RTCSetNodeBoundsFunc` callback function, get a pointer to the
+node as input (`nodePtr` argument), an array of pointers to the
+bounding boxes of the children (`bounds` argument), and the size of
+this array (`numChildren` argument).
+
+The `RTCCreateLeafFunc` callback additionally get an array of
+primitives as input (`primitives` argument), and the size of this
+array (`numPrimitives` argument). The callback should read the
+`geomID` and `primID` members from the passed primitives to construct
+the leaf.
+
+The `RTCSplitPrimitiveFunc` callback is invoked in high quality mode
+to split a primitive (`prim` argument) at some specified position
+(`pos` argument) and dimension (`dim` argument). The callback should
+return bounds of the clipped left and right part of the primitive
+(`lbounds` and `rbounds` arguments).
+
+There is an optional progress callback function that can be used to
+get progress on the BVH build.
+
+    typedef void (*RTCBuildProgressFunc) (size_t N, void* userPtr);
+
+This progress function is called with a number `N` of primitives the
+build is finished for. Accumulating over all invokations will sum up
+to the number of primitives passed to be BVH build function.
+
+All callback functions are typically called from multiple threads,
+thus their implementation has to be thread safe.
+
+All callback function get a user defined pointer (`userPtr` argument)
+as input which is provided to the `rtcBuildBVH` call. This pointer can
+be used to access the application scene object inside the callback
+functions.
+
+The BVH build is invoked using the `rtcBuildBVH` function:
+
+    void* rtcBuildBVH(RTCBVH bvh,                             //!< BVH to build
+                      const RTCBuildSettings& settings,       //!< settings for BVH builder
+                      RTCBuildPrimitive* primitives,          //!< list of input primitives
+                      size_t numPrimitives,                   //!< number of input primitives
+                      RTCCreateNodeFunc createNode,           //!< creates a node
+                      RTCSetNodeChildrenFunc setNodeChildren, //!< sets pointer to a child
+                      RTCSetNodeBoundsFunc setNodeBounds,     //!< sets bound of a child
+                      RTCCreateLeafFunc createLeaf,           //!< creates a leaf
+                      RTCSplitPrimitiveFunc splitPrimitive,   //!< splits a primitive into two halves
+                      RTCBuildProgressFunc buildProgress      //!< used to report build progress
+                      void* userPtr);                         //!< user pointer passed to callback functions
+
+The function gets passed the BVH objects (`bvh` argument), the build
+settings to use (`settings` argument), the array of primitives
+(`primitives` argument) and its size (`numPrimitives` argument), the
+previously described callback function pointers, and a user defined
+pointer (`userPtr` argument) that is passed to all callback
+functions. The function pointer to the primitive split function
+(`splitPrimitive` argument) may be `NULL`, however, then no spatial
+splitting in high quality mode is possible. The function pointer used
+to report the build progress (`buildProgress` argument) is optional
+and may also be `NULL`.
+
+For static scenes that do not require a further `rtcBuildBVH` call one
+should use the `rtcMakeStatic` function after the build which clears
+some internal data.
+
+    void rtcMakeStaticBVH(RTCBVH bvh);
+Embree Tutorials
+================
+
+Embree comes with a set of tutorials aimed at helping users understand
+how Embree can be used and extended. All tutorials exist in an ISPC and
+C++ version to demonstrate the two versions of the API. Look for files
+named `tutorialname_device.ispc` for the ISPC implementation of the
+tutorial, and files named `tutorialname_device.cpp` for the single ray C++
+version of the tutorial. To start the C++ version use the `tutorialname`
+executables, to start the ISPC version use the `tutorialname_ispc`
+executables.
+
+For all tutorials, you can select an initial camera using the `-vp`
+(camera position), `-vi` (camera look-at point), `-vu` (camera up
+vector), and `-fov` (vertical field of view) command line parameters:
+
+    ./triangle_geometry -vp 10 10 10 -vi 0 0 0
+
+You can select the initial windows size using the `-size` command line
+parameter, or start the tutorials in fullscreen using the `-fullscreen`
+parameter:
+
+    ./triangle_geometry -size 1024 1024
+    ./triangle_geometry -fullscreen
+
+Implementation specific parameters can be passed to the ray tracing core
+through the `-rtcore` command line parameter, e.g.:
+
+    ./triangle_geometry -rtcore verbose=2,threads=1,accel=bvh4.triangle1
+
+The navigation in the interactive display mode follows the camera orbit
+model, where the camera revolves around the current center of interest.
+With the left mouse button you can rotate around the center of interest
+(the point initially set with `-vi`). Holding Control pressed while
+clicking the left mouse button rotates the camera around its location.
+You can also use the arrow keys for navigation.
+
+You can use the following keys:
+
+F1
+:   Default shading
+
+F2
+:   Gray EyeLight shading
+
+F3
+:   Wireframe shading
+
+F4
+:   UV Coordinate visualization
+
+F5
+:   Geometry normal visualization
+
+F6
+:   Geometry ID visualization
+
+F7
+:   Geometry ID and Primitive ID visualization
+
+F8
+:   Simple shading with 16 rays per pixel for benchmarking.
+
+F9
+:   Switches to render cost visualization. Pressing again reduces
+    brightness.
+
+F10
+:   Switches to render cost visualization. Pressing again increases
+    brightness.
+
+f
+:   Enters or leaves full screen mode.
+
+c
+:   Prints camera parameters.
+
+ESC
+:   Exits the tutorial.
+
+q
+:   Exits the tutorial.
+
+Triangle Geometry
+-----------------
+
+![][imgTriangleGeometry]
+
+This tutorial demonstrates the creation of a static cube and ground
+plane using triangle meshes. It also demonstrates the use of the
+`rtcIntersect` and `rtcOccluded` functions to render primary visibility
+and hard shadows. The cube sides are colored based on the ID of the hit
+primitive.
+
+Dynamic Scene
+-------------
+
+![][imgDynamicScene]
+
+This tutorial demonstrates the creation of a dynamic scene, consisting
+of several deformed spheres. Half of the spheres use the
+`RTC_GEOMETRY_DEFORMABLE` flag, which allows Embree to use a refitting
+strategy for these spheres, the other half uses the
+`RTC_GEOMETRY_DYNAMIC` flag, causing a rebuild of their spatial data
+structure each frame. The spheres are colored based on the ID of the hit
+sphere geometry.
+
+User Geometry
+-------------
+
+![][imgUserGeometry]
+
+This tutorial shows the use of user defined geometry, to re-implement
+instancing and to add analytic spheres. A two level scene is created,
+with a triangle mesh as ground plane, and several user geometries, that
+instance other scenes with a small number of spheres of different kind.
+The spheres are colored using the instance ID and geometry ID of the hit
+sphere, to demonstrate how the same geometry, instanced in different
+ways can be distinguished.
+
+Viewer
+------
+
+![][imgViewer]
+
+This tutorial demonstrates a simple OBJ viewer that traces primary
+visibility rays only. A scene consisting of multiple meshes is created,
+each mesh sharing the index and vertex buffer with the application.
+Demonstrated is also how to support additional per vertex data, such as
+shading normals.
+
+You need to specify an OBJ file at the command line for this tutorial to
+work:
+
+    ./viewer -i model.obj
+
+Stream Viewer
+-------------
+
+![][imgViewerStream]
+
+This tutorial demonstrates a simple OBJ viewer that demonstrates the
+use of ray streams. You need to specify an OBJ file at the command
+line for this tutorial to work:
+
+    ./viewer_stream -i model.obj
+
+Instanced Geometry
+------------------
+
+![][imgInstancedGeometry]
+
+This tutorial demonstrates the in-build instancing feature of Embree, by
+instancing a number of other scenes build from triangulated spheres. The
+spheres are again colored using the instance ID and geometry ID of the
+hit sphere, to demonstrate how the same geometry, instanced in different
+ways can be distinguished.
+
+Intersection Filter
+-------------------
+
+![][imgIntersectionFilter]
+
+This tutorial demonstrates the use of filter callback functions to
+efficiently implement transparent objects. The filter function used for
+primary rays, lets the ray pass through the geometry if it is entirely
+transparent. Otherwise the shading loop handles the transparency
+properly, by potentially shooting secondary rays. The filter function
+used for shadow rays accumulates the transparency of all surfaces along
+the ray, and terminates traversal if an opaque occluder is hit.
+
+Pathtracer
+----------
+
+![][imgPathtracer]
+
+This tutorial is a simple path tracer, building on the viewer tutorial.
+
+You need to specify an OBJ file and light source at the command line for
+this tutorial to work:
+
+    ./pathtracer -i model.obj -ambientlight 1 1 1
+
+As example models we provide the "Austrian Imperial Crown" model by
+[Martin Lubich](www.loramel.net) and the "Asian Dragon" model from the
+[Stanford 3D Scanning Repository](http://graphics.stanford.edu/data/3Dscanrep/).
+
+[crown.zip](https://github.com/embree/models/releases/download/release/crown.zip)
+
+[asian_dragon.zip](https://github.com/embree/models/releases/download/release/asian_dragon.zip)
+
+To render these models execute the following:
+
+    ./pathtracer -c crown/crown.ecs
+    ./pathtracer -c asian_dragon/asian_dragon.ecs
+
+Hair
+----
+
+![][imgHairGeometry]
+
+This tutorial demonstrates the use of the hair geometry to render a
+hairball.
+
+Bézier Curves
+-------------
+
+![][imgCurveGeometry]
+
+This tutorial demonstrates the use of the Bézier curve geometry.
+
+Subdivision Geometry
+--------------------
+
+![][imgSubdivisionGeometry]
+
+This tutorial demonstrates the use of Catmull Clark subdivision
+surfaces. Per default the edge tessellation level is set adaptively
+based on the distance to the camera origin. Embree currently supports
+three different modes for efficiently handling subdivision surfaces in
+various rendering scenarios. These three modes can be selected at the
+command line, e.g. `-lazy` builds internal per subdivision patch data
+structures on demand, `-cache` uses a small (per thread) tessellation
+cache for caching per patch data, and `-pregenerate` to generate and
+store most per patch data during the initial build process. The
+`cache` mode is most effective for coherent rays while providing a
+fixed memory footprint. The `pregenerate` modes is most effective for
+incoherent ray distributions while requiring more memory. The `lazy`
+mode works similar to the `pregenerate` mode but provides a middle
+ground in terms of memory consumption as it only builds and stores
+data only when the corresponding patch is accessed during the ray
+traversal. The `cache` mode is currently a bit more efficient at
+handling dynamic scenes where only the edge tessellation levels are
+changing per frame.
+
+Displacement Geometry
+---------------------
+
+![][imgDisplacementGeometry]
+
+This tutorial demonstrates the use of Catmull Clark subdivision
+surfaces with procedural displacement mapping using a constant edge
+tessellation level.
+
+Motion Blur Geometry
+--------------------
+
+![][imgMotionBlurGeometry]
+
+This tutorial demonstrates rendering of motion blur using the multi
+segment motion blur feature. Shown is motion blur of a triangle mesh,
+quad mesh, subdivision surface, line segments, hair geometry, bezier
+curves, instantiated triangle mesh where the instance moves,
+instantiated quad mesh where the instance and the quads move, and user
+geometry.
+
+The number of time steps used can be configured using the --time-steps
+<int> and --time-steps2 <int> command line parameters, and the geometry
+can be rendered at a specific time using the the --time <float> command
+line parameter.
+
+Interpolation
+-------------
+
+![][imgInterpolation]
+
+This tutorial demonstrates interpolation of user defined per vertex data.
+
+BVH Builder
+-----------
+
+This tutorial demonstrates how to use the templated hierarchy builders
+of Embree to build a bounding volume hierarchy with a user defined
+memory layout using a high quality SAH builder and very fast morton
+builder.
+
+BVH Access
+-----------
+
+This tutorial demonstrates how to access the internal triangle
+acceleration structure build by Embree. Please be aware that the
+internal Embree data structures might change between Embree updates.
+
+Find Embree
+-----------
+
+This tutorial demonstrates how to use the `FIND_PACKAGE` CMake feature
+to use an installed Embree. Under Linux and Mac OS X the tutorial finds
+the Embree installation automatically, under Windows the `embree_DIR`
+CMake variable has to be set to the following folder of the Embree
+installation: `C:\Program Files\Intel\Embree
+X.Y.Z\lib\cmake\embree-X.Y.Z`.
+[Embree API]: #embree-api
+[Ray Layout]: #ray-layout
+[Extending the Ray Structure]: #extending-the-ray-structure
+[Embree Example Renderer]: https://embree.github.io/renderer.html
+[Triangle Geometry]: #triangle-geometry
+[Stream Viewer]: #stream-viewer
+[User Geometry]: #user-geometry
+[Instanced Geometry]: #instanced-geometry
+[Intersection Filter]: #intersection-filter
+[Hair]: #hair
+[Curves]: #bézier-curves
+[Subdivision Geometry]: #subdivision-geometry
+[Displacement Geometry]: #displacement-geometry
+[BVH Builder]: #bvh-builder
+[Interpolation]: #interpolation
+[Individual Contributor License Agreement (ICLA)]: https://embree.github.io/data/Embree-ICLA.pdf
+[Corporate Contributor License Agreement (CCLA)]: https://embree.github.io/data/Embree-CCLA.pdf
+[imgTriangleUV]: https://embree.github.io/images/triangle_uv.png
+[imgQuadUV]: https://embree.github.io/images/quad_uv.png
+[imgTriangleGeometry]: https://embree.github.io/images/triangle_geometry.jpg
+[imgDynamicScene]: https://embree.github.io/images/dynamic_scene.jpg
+[imgUserGeometry]: https://embree.github.io/images/user_geometry.jpg
+[imgViewer]: https://embree.github.io/images/viewer.jpg
+[imgViewerStream]: https://embree.github.io/images/viewer_stream.jpg
+[imgInstancedGeometry]: https://embree.github.io/images/instanced_geometry.jpg
+[imgIntersectionFilter]: https://embree.github.io/images/intersection_filter.jpg
+[imgPathtracer]: https://embree.github.io/images/pathtracer.jpg
+[imgHairGeometry]: https://embree.github.io/images/hair_geometry.jpg
+[imgCurveGeometry]: https://embree.github.io/images/curve_geometry.jpg
+[imgSubdivisionGeometry]: https://embree.github.io/images/subdivision_geometry.jpg
+[imgDisplacementGeometry]: https://embree.github.io/images/displacement_geometry.jpg
+[imgMotionBlurGeometry]: https://embree.github.io/images/motion_blur_geometry.jpg
+[imgInterpolation]: https://embree.github.io/images/interpolation.jpg

+ 21 - 0
Source/ThirdParty/embree/common/CMakeLists.txt

@@ -0,0 +1,21 @@
+## ======================================================================== ##
+## Copyright 2009-2017 Intel Corporation                                    ##
+##                                                                          ##
+## Licensed under the Apache License, Version 2.0 (the "License");          ##
+## you may not use this file except in compliance with the License.         ##
+## You may obtain a copy of the License at                                  ##
+##                                                                          ##
+##     http://www.apache.org/licenses/LICENSE-2.0                           ##
+##                                                                          ##
+## Unless required by applicable law or agreed to in writing, software      ##
+## distributed under the License is distributed on an "AS IS" BASIS,        ##
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ##
+## See the License for the specific language governing permissions and      ##
+## limitations under the License.                                           ##
+## ======================================================================== ##
+
+ADD_SUBDIRECTORY(sys)
+ADD_SUBDIRECTORY(simd)
+ADD_SUBDIRECTORY(lexers)
+ADD_SUBDIRECTORY(tasking)
+ADD_SUBDIRECTORY(algorithms)

+ 32 - 0
Source/ThirdParty/embree/common/algorithms/CMakeLists.txt

@@ -0,0 +1,32 @@
+## ======================================================================== ##
+## Copyright 2009-2017 Intel Corporation                                    ##
+##                                                                          ##
+## Licensed under the Apache License, Version 2.0 (the "License");          ##
+## you may not use this file except in compliance with the License.         ##
+## You may obtain a copy of the License at                                  ##
+##                                                                          ##
+##     http://www.apache.org/licenses/LICENSE-2.0                           ##
+##                                                                          ##
+## Unless required by applicable law or agreed to in writing, software      ##
+## distributed under the License is distributed on an "AS IS" BASIS,        ##
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ##
+## See the License for the specific language governing permissions and      ##
+## limitations under the License.                                           ##
+## ======================================================================== ##
+
+ADD_LIBRARY(algorithms OBJECT
+ parallel_for.cpp
+ parallel_reduce.cpp
+ parallel_prefix_sum.cpp
+ parallel_for_for.cpp
+ parallel_for_for_prefix_sum.cpp
+ parallel_partition.cpp
+ parallel_sort.cpp
+ parallel_set.cpp
+ parallel_map.cpp
+ parallel_filter.cpp
+)
+
+SET_PROPERTY(TARGET algorithms PROPERTY FOLDER common)
+
+ 

+ 67 - 0
Source/ThirdParty/embree/common/algorithms/parallel_filter.cpp

@@ -0,0 +1,67 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#include "parallel_filter.h"
+#include "../sys/regression.h"
+#include <map>
+
+namespace embree
+{
+  struct parallel_filter_regression_test : public RegressionTest
+  {
+    parallel_filter_regression_test(const char* name) : RegressionTest(name) {
+      registerRegressionTest(this);
+    }
+    
+    bool run ()
+    {
+      bool passed = true;
+      auto pred = [&]( uint32_t v ) { return (v & 0x3) == 0; };
+      
+      for (size_t N=10; N<1000000; N=size_t(2.1*N))
+      {
+	/* initialize array with random numbers */
+	std::vector<uint32_t> src(N);
+        std::map<uint32_t,int> m;
+	for (size_t i=0; i<N; i++) src[i] = rand();
+
+        /* count elements up */
+	for (size_t i=0; i<N; i++)
+          if (pred(src[i]))
+            m[src[i]] = 0;
+        for (size_t i=0; i<N; i++)
+          if (pred(src[i]))
+            m[src[i]]++;
+
+        /* filter array */
+        //uint32_t M = sequential_filter(src.data(),size_t(0),N,pred);
+        uint32_t M = parallel_filter(src.data(),size_t(0),N,size_t(1024),pred);
+        
+	/* check if filtered data is correct */
+	for (size_t i=0; i<M; i++) {
+          passed &= pred(src[i]);
+          m[src[i]]--;
+        }
+	for (size_t i=0; i<M; i++)
+          passed &= (m[src[i]] == 0);
+      }
+
+      return passed;
+    }
+  };
+
+  parallel_filter_regression_test parallel_filter_regression("parallel_filter_regression");
+}

+ 103 - 0
Source/ThirdParty/embree/common/algorithms/parallel_filter.h

@@ -0,0 +1,103 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#pragma once
+
+#include "parallel_for.h"
+
+namespace embree
+{
+  template<typename Ty, typename Index, typename Predicate>
+    inline Index sequential_filter( Ty* data, const Index first, const Index last, const Predicate& predicate)
+  {
+    Index j = first;
+    for (Index i=first; i<last; i++)
+      if (predicate(data[i]))
+        data[j++] = data[i];
+
+    return j;
+  }
+
+  template<typename Ty, typename Index, typename Predicate>
+    inline Index parallel_filter( Ty* data, const Index begin, const Index end, const Index minStepSize, const Predicate& predicate)
+  {
+    /* sequential fallback */
+    if (end-begin <= minStepSize)
+      return sequential_filter(data,begin,end,predicate);
+
+    /* calculate number of tasks to use */
+    enum { MAX_TASKS = 512 };
+    const Index numThreads = TaskScheduler::threadCount();
+    const Index numBlocks  = (end-begin+minStepSize-1)/minStepSize;
+    const Index taskCount  = min(numThreads,numBlocks,(Index)MAX_TASKS);
+
+    /* filter blocks */
+    Index nused[MAX_TASKS];
+    Index nfree[MAX_TASKS];
+    parallel_for(taskCount, [&](const Index taskIndex)
+    {
+      const Index i0 = begin+(taskIndex+0)*(end-begin)/taskCount;
+      const Index i1 = begin+(taskIndex+1)*(end-begin)/taskCount;
+      const Index i2 = sequential_filter(data,i0,i1,predicate);
+      nused[taskIndex] = i2-i0;
+      nfree[taskIndex] = i1-i2;
+    });
+
+    /* calculate offsets */
+    Index sused=0;
+    Index sfree=0;
+    Index pfree[MAX_TASKS];
+    for (Index i=0; i<taskCount; i++) 
+    {
+      sused+=nused[i];
+      Index cfree = nfree[i]; pfree[i] = sfree; sfree+=cfree;
+    }
+
+    /* return if we did not filter out any element */
+    assert(sfree <= end-begin);
+    assert(sused <= end-begin);
+    if (sused == end-begin)
+      return end;
+
+    /* otherwise we have to copy misplaced elements around */
+    parallel_for(taskCount, [&](const Index taskIndex)
+    {
+      /* destination to write elements to */
+      Index dst = (taskIndex+0)*(end-begin)/taskCount+nused[taskIndex];
+      Index dst_end = min(dst+nfree[taskIndex],sused);
+      if (dst_end <= dst) return;
+
+      /* range of misplaced elements to copy to destination */
+      Index r0 = pfree[taskIndex];
+      Index r1 = r0+dst_end-dst;
+
+      /* find range in misplaced elements in back to front order */
+      Index k0=0;
+      for (Index i=taskCount-1; i>0; i--)
+      {
+        if (k0 > r1) break;
+        Index k1 = k0+nused[i];
+        Index src = begin+(i+0)*(end-begin)/taskCount+nused[i];
+        for (Index i=max(r0,k0); i<min(r1,k1); i++) {
+          data[dst++] = data[src-i+k0-1];
+        }
+        k0 = k1;
+      }
+    });
+
+    return begin+sused;
+  }
+}

+ 61 - 0
Source/ThirdParty/embree/common/algorithms/parallel_for.cpp

@@ -0,0 +1,61 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#include "parallel_for.h"
+#include "../sys/regression.h"
+
+namespace embree
+{
+  struct parallel_for_regression_test : public RegressionTest
+  {
+    parallel_for_regression_test(const char* name) : RegressionTest(name) {
+      registerRegressionTest(this);
+    }
+    
+    bool run ()
+    {
+      bool passed = true;
+
+      const size_t M = 10;
+      for (size_t N=10; N<10000000; N=size_t(2.1*N))
+      {
+        /* sequentially calculate sum of squares */
+        size_t sum0 = 0;
+        for (size_t i=0; i<N; i++) {
+          sum0 += i*i;
+        }
+
+        /* parallel calculation of sum of squares */
+        for (size_t m=0; m<M; m++)
+        {
+          std::atomic<size_t> sum1(0);
+          parallel_for( size_t(0), size_t(N), size_t(1024), [&](const range<size_t>& r) 
+          {
+            size_t s = 0;
+            for (size_t i=r.begin(); i<r.end(); i++) 
+              s += i*i;
+            sum1 += s;
+          });
+          passed = sum0 == sum1;
+        }
+      }
+      
+      return passed;
+    }
+  };
+
+  parallel_for_regression_test parallel_for_regression("parallel_for_regression_test");
+}

+ 133 - 0
Source/ThirdParty/embree/common/algorithms/parallel_for.h

@@ -0,0 +1,133 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#pragma once
+
+#include "../common/tasking/taskscheduler.h"
+#include "../sys/array.h"
+#include "../math/math.h"
+#include "../common/math/range.h"
+
+namespace embree
+{
+  /* parallel_for without range */
+  template<typename Index, typename Func>
+    __forceinline void parallel_for( const Index N, const Func& func)
+  {
+#if defined(TASKING_INTERNAL)
+    if (N) {
+      TaskScheduler::spawn(Index(0),N,Index(1),[&] (const range<Index>& r) {
+          assert(r.size() == 1);
+          func(r.begin());
+        });
+      if (!TaskScheduler::wait())
+        throw std::runtime_error("task cancelled");
+    }
+    
+#elif defined(TASKING_TBB)
+    tbb::parallel_for(Index(0),N,Index(1),[&](Index i) { 
+	func(i);
+      });
+    if (tbb::task::self().is_cancelled())
+      throw std::runtime_error("task cancelled");
+
+#elif defined(TASKING_PPL)
+    concurrency::parallel_for(Index(0),N,Index(1),[&](Index i) { 
+        func(i);
+      });
+#else
+#  error "no tasking system enabled"
+#endif
+  }
+  
+  /* parallel for with range and granulatity */
+  template<typename Index, typename Func>
+    __forceinline void parallel_for( const Index first, const Index last, const Index minStepSize, const Func& func)
+  {
+    assert(first <= last);
+#if defined(TASKING_INTERNAL)
+    TaskScheduler::spawn(first,last,minStepSize,func);
+    if (!TaskScheduler::wait())
+      throw std::runtime_error("task cancelled");
+
+#elif defined(TASKING_TBB)
+    tbb::parallel_for(tbb::blocked_range<Index>(first,last,minStepSize),[&](const tbb::blocked_range<Index>& r) { 
+        func(range<Index>(r.begin(),r.end()));
+      });
+    if (tbb::task::self().is_cancelled())
+      throw std::runtime_error("task cancelled");
+
+#elif defined(TASKING_PPL)
+    concurrency::parallel_for(first, last, Index(1) /*minStepSize*/, [&](Index i) { 
+        func(range<Index>(i,i+1)); 
+      });
+
+#else
+#  error "no tasking system enabled"
+#endif
+  }
+  
+  /* parallel for with range */
+  template<typename Index, typename Func>
+    __forceinline void parallel_for( const Index first, const Index last, const Func& func)
+  {
+    assert(first <= last);
+    parallel_for(first,last,(Index)1,func);
+  }
+
+#if defined(TASKING_TBB) && (TBB_INTERFACE_VERSION > 4001)
+
+  template<typename Index, typename Func>
+    __forceinline void parallel_for_static( const Index N, const Func& func)
+  {
+    tbb::parallel_for(Index(0),N,Index(1),[&](Index i) { 
+	func(i);
+      },tbb::simple_partitioner());
+    if (tbb::task::self().is_cancelled())
+      throw std::runtime_error("task cancelled");
+  }
+
+  typedef tbb::affinity_partitioner affinity_partitioner;
+
+  template<typename Index, typename Func>
+    __forceinline void parallel_for_affinity( const Index N, const Func& func, tbb::affinity_partitioner& ap)
+  {
+    tbb::parallel_for(Index(0),N,Index(1),[&](Index i) { 
+	func(i);
+      },ap);
+    if (tbb::task::self().is_cancelled())
+      throw std::runtime_error("task cancelled");
+  }
+
+#else
+
+  template<typename Index, typename Func>
+    __forceinline void parallel_for_static( const Index N, const Func& func) 
+  {
+    parallel_for(N,func);
+  }
+
+  struct affinity_partitioner {
+  };
+
+  template<typename Index, typename Func>
+    __forceinline void parallel_for_affinity( const Index N, const Func& func, affinity_partitioner& ap) 
+  {
+    parallel_for(N,func);
+  }
+
+#endif
+}

+ 76 - 0
Source/ThirdParty/embree/common/algorithms/parallel_for_for.cpp

@@ -0,0 +1,76 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#include "parallel_for_for.h"
+#include "../sys/regression.h"
+
+namespace embree
+{
+  struct parallel_for_for_regression_test : public RegressionTest
+  {
+    parallel_for_for_regression_test(const char* name) : RegressionTest(name) {
+      registerRegressionTest(this);
+    }
+    
+    bool run ()
+    {
+      bool passed = true;
+
+      /* create vector with random numbers */
+      size_t sum0 = 0;
+      size_t K = 0;
+      const size_t M = 1000;
+      std::vector<std::vector<size_t>* > array2(M);
+      for (size_t i=0; i<M; i++) {
+        const size_t N = rand() % 1024;
+        K+=N;
+        array2[i] = new std::vector<size_t>(N);
+        for (size_t j=0; j<N; j++) 
+          sum0 += (*array2[i])[j] = rand();
+      }
+
+      /* array to test global index */
+      std::vector<atomic<size_t>> verify_k(K);
+      for (size_t i=0; i<K; i++) verify_k[i].store(0);
+
+      /* add all numbers using parallel_for_for */
+      std::atomic<size_t> sum1(0);
+      parallel_for_for( array2, size_t(1), [&](std::vector<size_t>* v, const range<size_t>& r, size_t k) -> size_t
+      {
+        size_t s = 0;
+	for (size_t i=r.begin(); i<r.end(); i++) {
+	  s += (*v)[i];
+          verify_k[k++]++;
+        }
+        sum1 += s;
+	return sum1;
+      });
+      passed &= (sum0 == sum1);
+
+      /* check global index */
+      for (size_t i=0; i<K; i++) 
+        passed &= (verify_k[i] == 1);
+
+      /* delete vectors again */
+      for (size_t i=0; i<array2.size(); i++)
+	delete array2[i];
+      
+      return passed;
+    }
+  };
+
+  parallel_for_for_regression_test parallel_for_for_regression("parallel_for_for_regression_test");
+}

+ 162 - 0
Source/ThirdParty/embree/common/algorithms/parallel_for_for.h

@@ -0,0 +1,162 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#pragma once
+
+#include "parallel_for.h"
+
+namespace embree
+{
+  template<typename ArrayArray, typename Func>
+    __forceinline void sequential_for_for( ArrayArray& array2, const size_t minStepSize, const Func& func ) 
+  {
+    size_t k=0;
+    for (size_t i=0; i!=array2.size(); ++i) {
+      const size_t N = array2[i]->size();
+      if (N) func(array2[i],range<size_t>(0,N),k);
+      k+=N;
+    }
+  }
+
+  class ParallelForForState
+  {
+  public:
+
+    enum { MAX_TASKS = 512 };
+
+    __forceinline ParallelForForState () 
+      : taskCount(0) {}
+
+    template<typename ArrayArray>
+      __forceinline ParallelForForState (ArrayArray& array2, const size_t minStepSize) {
+      init(array2,minStepSize);
+    } 
+
+    template<typename ArrayArray>
+      __forceinline void init ( ArrayArray& array2, const size_t minStepSize )
+    {
+      /* first calculate total number of elements */
+      size_t N = 0;
+      for (size_t i=0; i<array2.size(); i++) {
+	N += array2[i] ? array2[i]->size() : 0;
+      }
+      this->N = N;
+
+      /* calculate number of tasks to use */
+      const size_t numThreads = TaskScheduler::threadCount();
+      const size_t numBlocks  = (N+minStepSize-1)/minStepSize;
+      taskCount = max(size_t(1),min(numThreads,numBlocks,size_t(ParallelForForState::MAX_TASKS)));
+      
+      /* calculate start (i,j) for each task */
+      size_t taskIndex = 0;
+      i0[taskIndex] = 0;
+      j0[taskIndex] = 0;
+      size_t k0 = (++taskIndex)*N/taskCount;
+      for (size_t i=0, k=0; taskIndex < taskCount; i++) 
+      {
+	assert(i<array2.size());
+	size_t j=0, M = array2[i] ? array2[i]->size() : 0;
+	while (j<M && k+M-j >= k0 && taskIndex < taskCount) {
+	  assert(taskIndex<taskCount);
+	  i0[taskIndex] = i;
+	  j0[taskIndex] = j += k0-k;
+	  k=k0;
+	  k0 = (++taskIndex)*N/taskCount;
+	}
+	k+=M-j;
+      }
+    }
+
+    __forceinline size_t size() const {
+      return N;
+    }
+    
+  public:
+    size_t i0[MAX_TASKS];
+    size_t j0[MAX_TASKS];
+    size_t taskCount;
+    size_t N;
+  };
+
+  template<typename ArrayArray, typename Func>
+    __forceinline void parallel_for_for( ArrayArray& array2, const size_t minStepSize, const Func& func )
+  {
+    ParallelForForState state(array2,minStepSize);
+    
+    parallel_for(state.taskCount, [&](const size_t taskIndex) 
+    {
+      /* calculate range */
+      const size_t k0 = (taskIndex+0)*state.size()/state.taskCount;
+      const size_t k1 = (taskIndex+1)*state.size()/state.taskCount;
+      size_t i0 = state.i0[taskIndex];
+      size_t j0 = state.j0[taskIndex];
+
+      /* iterate over arrays */
+      size_t k=k0;
+      for (size_t i=i0; k<k1; i++) {
+        const size_t N =  array2[i] ? array2[i]->size() : 0;
+        const size_t r0 = j0, r1 = min(N,r0+k1-k);
+        if (r1 > r0) func(array2[i],range<size_t>(r0,r1),k);
+        k+=r1-r0; j0 = 0;
+      }
+    });
+  }
+
+  template<typename ArrayArray, typename Func>
+    __forceinline void parallel_for_for( ArrayArray& array2, const Func& func )
+  {
+    parallel_for_for(array2,1,func);
+  }
+
+  template<typename ArrayArray, typename Value, typename Func, typename Reduction>
+    __forceinline Value parallel_for_for_reduce( ArrayArray& array2, const size_t minStepSize, const Value& identity, const Func& func, const Reduction& reduction )
+  {
+    ParallelForForState state(array2,minStepSize);
+    Value temp[ParallelForForState::MAX_TASKS];
+
+    for (size_t i=0; i<state.taskCount; i++)
+      temp[i] = identity;
+    
+    parallel_for(state.taskCount, [&](const size_t taskIndex) 
+    {
+      /* calculate range */
+      const size_t k0 = (taskIndex+0)*state.size()/state.taskCount;
+      const size_t k1 = (taskIndex+1)*state.size()/state.taskCount;
+      size_t i0 = state.i0[taskIndex];
+      size_t j0 = state.j0[taskIndex];
+
+      /* iterate over arrays */
+      size_t k=k0;
+      for (size_t i=i0; k<k1; i++) {
+        const size_t N =  array2[i] ? array2[i]->size() : 0;
+        const size_t r0 = j0, r1 = min(N,r0+k1-k);
+        if (r1 > r0) temp[taskIndex] = reduction(temp[taskIndex],func(array2[i],range<size_t>(r0,r1),k));
+        k+=r1-r0; j0 = 0;
+      }
+    });
+
+    Value ret = identity;
+    for (size_t i=0; i<state.taskCount; i++)
+      ret = reduction(ret,temp[i]);
+    return ret;
+  }
+
+  template<typename ArrayArray, typename Value, typename Func, typename Reduction>
+    __forceinline Value parallel_for_for_reduce( ArrayArray& array2, const Value& identity, const Func& func, const Reduction& reduction)
+  {
+    return parallel_for_for_reduce(array2,1,identity,func,reduction);
+  }
+}

+ 98 - 0
Source/ThirdParty/embree/common/algorithms/parallel_for_for_prefix_sum.cpp

@@ -0,0 +1,98 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#include "parallel_for_for_prefix_sum.h"
+#include "../sys/regression.h"
+
+namespace embree
+{
+  struct parallel_for_for_prefix_sum_regression_test : public RegressionTest
+  {
+    parallel_for_for_prefix_sum_regression_test(const char* name) : RegressionTest(name) {
+      registerRegressionTest(this);
+    }
+    
+    bool run ()
+    {
+      bool passed = true;
+
+      /* create vector with random numbers */
+      const size_t M = 10;
+      std::vector<atomic<size_t>> flattened;
+      typedef std::vector<std::vector<size_t>* > ArrayArray;
+      ArrayArray array2(M);
+      size_t K = 0;
+      for (size_t i=0; i<M; i++) {
+        const size_t N = rand() % 10;
+        K += N;
+        array2[i] = new std::vector<size_t>(N);
+        for (size_t j=0; j<N; j++) 
+          (*array2[i])[j] = rand() % 10;
+      }
+  
+      /* array to test global index */
+      std::vector<atomic<size_t>> verify_k(K);
+      for (size_t i=0; i<K; i++) verify_k[i].store(0);
+
+      ParallelForForPrefixSumState<size_t> state(array2,size_t(1));
+  
+      /* dry run only counts */
+      size_t S = parallel_for_for_prefix_sum( state, array2, size_t(0), [&](std::vector<size_t>* v, const range<size_t>& r, size_t k, const size_t base) -> size_t
+      {
+        size_t s = 0;
+	for (size_t i=r.begin(); i<r.end(); i++) {
+          s += (*v)[i];
+          verify_k[k++]++;
+        }
+        return s;
+      }, [](size_t v0, size_t v1) { return v0+v1; });
+      
+      /* create properly sized output array */
+      flattened.resize(S);
+      for (auto& a : flattened) a.store(0);
+
+      /* now we actually fill the flattened array */
+      parallel_for_for_prefix_sum( state, array2, size_t(0), [&](std::vector<size_t>* v, const range<size_t>& r, size_t k, const size_t base) -> size_t
+      {
+        size_t s = 0;
+	for (size_t i=r.begin(); i<r.end(); i++) {
+          for (size_t j=0; j<(*v)[i]; j++) {
+            flattened[base+s+j]++;
+          }
+          s += (*v)[i];
+          verify_k[k++]++;
+        }
+        return s;
+      }, [](size_t v0, size_t v1) { return v0+v1; });
+
+      /* check global index */
+      for (size_t i=0; i<K; i++) 
+        passed &= (verify_k[i] == 2);
+
+      /* check if each element was assigned exactly once */
+      for (size_t i=0; i<flattened.size(); i++)
+        passed &= (flattened[i] == 1);
+      
+      /* delete arrays again */
+      for (size_t i=0; i<array2.size(); i++)
+	delete array2[i];
+
+      return passed;
+    }
+  };
+
+  parallel_for_for_prefix_sum_regression_test parallel_for_for_prefix_sum_regression("parallel_for_for_prefix_sum_regression_test");
+}

+ 80 - 0
Source/ThirdParty/embree/common/algorithms/parallel_for_for_prefix_sum.h

@@ -0,0 +1,80 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#pragma once
+
+#include "parallel_for_for.h"
+#include "parallel_prefix_sum.h"
+
+namespace embree
+{
+  template<typename Value>
+    struct ParallelForForPrefixSumState : public ParallelForForState
+  {
+    __forceinline ParallelForForPrefixSumState () {}
+
+    template<typename ArrayArray>
+      __forceinline ParallelForForPrefixSumState (ArrayArray& array2, const size_t minStepSize)
+      : ParallelForForState(array2,minStepSize) {}
+
+    ParallelPrefixSumState<Value> prefix_state;
+  };
+  
+  template<typename ArrayArray, typename Index, typename Value, typename Func, typename Reduction>
+    __forceinline Value parallel_for_for_prefix_sum( ParallelForForPrefixSumState<Value>& state, ArrayArray& array2, Index minStepSize, 
+						     const Value& identity, const Func& func, const Reduction& reduction)
+  {
+    /* calculate number of tasks to use */
+    const size_t taskCount = state.taskCount;
+    /* perform parallel prefix sum */
+    parallel_for(taskCount, [&](const size_t taskIndex)
+    {
+      const size_t k0 = (taskIndex+0)*state.size()/taskCount;
+      const size_t k1 = (taskIndex+1)*state.size()/taskCount;
+      size_t i0 = state.i0[taskIndex];
+      size_t j0 = state.j0[taskIndex];
+
+      /* iterate over arrays */
+      size_t k=k0;
+      Value N=identity;
+      for (size_t i=i0; k<k1; i++) {
+	const size_t size = array2[i] ? array2[i]->size() : 0;
+        const size_t r0 = j0, r1 = min(size,r0+k1-k);
+        if (r1 > r0) N = reduction(N, func(array2[i],range<size_t>(r0,r1),k,reduction(state.prefix_state.sums[taskIndex],N)));
+        k+=r1-r0; j0 = 0;
+      }
+      state.prefix_state.counts[taskIndex] = N;
+    });
+
+    /* calculate prefix sum */
+    Value sum=identity;
+    for (size_t i=0; i<taskCount; i++)
+    {
+      const Value c = state.prefix_state.counts[i];
+      state.prefix_state.sums[i] = sum;
+      sum=reduction(sum,c);
+    }
+
+    return sum;
+  }
+
+   template<typename ArrayArray, typename Value, typename Func, typename Reduction>
+    __forceinline Value parallel_for_for_prefix_sum( ParallelForForPrefixSumState<Value>& state, ArrayArray& array2, 
+						     const Value& identity, const Func& func, const Reduction& reduction)
+  {
+    return parallel_for_for_prefix_sum(state,array2,size_t(1),identity,func,reduction);
+  }
+}

+ 60 - 0
Source/ThirdParty/embree/common/algorithms/parallel_map.cpp

@@ -0,0 +1,60 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#include "parallel_map.h"
+#include "../sys/regression.h"
+
+namespace embree
+{
+  struct parallel_map_regression_test : public RegressionTest
+  {
+    parallel_map_regression_test(const char* name) : RegressionTest(name) {
+      registerRegressionTest(this);
+    }
+    
+    bool run ()
+    {
+      bool passed = true;
+
+      /* create key/value vectors with random numbers */
+      const size_t N = 10000;
+      std::vector<uint32_t> keys(N);
+      std::vector<uint32_t> vals(N);
+      for (size_t i=0; i<N; i++) keys[i] = 2*unsigned(i)*647382649;
+      for (size_t i=0; i<N; i++) std::swap(keys[i],keys[rand()%N]);
+      for (size_t i=0; i<N; i++) vals[i] = 2*rand();
+      
+      /* create map */
+      parallel_map<uint32_t,uint32_t> map;
+      map.init(keys,vals);
+
+      /* check that all keys are properly mapped */
+      for (size_t i=0; i<N; i++) {
+        const uint32_t* val = map.lookup(keys[i]);
+        passed &= val && (*val == vals[i]);
+      }
+
+      /* check that these keys are not in the map */
+      for (size_t i=0; i<N; i++) {
+        passed &= !map.lookup(keys[i]+1);
+      }
+
+      return passed;
+    }
+  };
+
+  parallel_map_regression_test parallel_map_regression("parallel_map_regression_test");
+}

+ 98 - 0
Source/ThirdParty/embree/common/algorithms/parallel_map.h

@@ -0,0 +1,98 @@
+// ======================================================================== //
+// Copyright 2009-2017 Intel Corporation                                    //
+//                                                                          //
+// Licensed under the Apache License, Version 2.0 (the "License");          //
+// you may not use this file except in compliance with the License.         //
+// You may obtain a copy of the License at                                  //
+//                                                                          //
+//     http://www.apache.org/licenses/LICENSE-2.0                           //
+//                                                                          //
+// Unless required by applicable law or agreed to in writing, software      //
+// distributed under the License is distributed on an "AS IS" BASIS,        //
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
+// See the License for the specific language governing permissions and      //
+// limitations under the License.                                           //
+// ======================================================================== //
+
+#pragma once
+
+#include "parallel_sort.h"
+
+namespace embree
+{
+  /*! implementation of a key/value map with parallel construction */
+  template<typename Key, typename Val>
+  class parallel_map
+  {
+    /* key/value pair to build the map */
+    struct KeyValue
+    {
+      __forceinline KeyValue () {}
+
+      __forceinline KeyValue (const Key key, const Val val)
+	: key(key), val(val) {}
+
+      __forceinline operator Key() const {
+	return key;
+      }
+
+    public:
+      Key key;
+      Val val;
+    };
+
+  public:
+    
+    /*! parallel map constructors */
+    parallel_map () {}
+
+    /*! construction from pair of vectors */
+    template<typename KeyVector, typename ValVector>
+      parallel_map (const KeyVector& keys, const ValVector& values) { init(keys,values); }
+
+    /*! initialized the parallel map from a vector with keys and values */
+    template<typename KeyVector, typename ValVector>
+      void init(const KeyVector& keys, const ValVector& values) 
+    {
+      /* reserve sufficient space for all data */
+      assert(keys.size() == values.size());
+      vec.resize(keys.size());
+      
+      /* generate key/value pairs */
+      parallel_for( size_t(0), keys.size(), size_t(4*4096), [&](const range<size_t>& r) {
+	for (size_t i=r.begin(); i<r.end(); i++)
+	  vec[i] = KeyValue((Key)keys[i],values[i]);
+      });
+
+      /* perform parallel radix sort of the key/value pairs */
+      std::vector<KeyValue> temp(keys.size());
+      radix_sort<KeyValue,Key>(vec.data(),temp.data(),keys.size());
+    }
+
+    /*! Returns a pointer to the value associated with the specified key. The pointer will be nullptr of the key is not contained in the map. */
+    __forceinline const Val* lookup(const Key& key) const 
+    {
+      typename std::vector<KeyValue>::const_iterator i = std::lower_bound(vec.begin(), vec.end(), key);
+      if (i == vec.end()) return nullptr;
+      if (i->key != key) return nullptr;
+      return &i->val;
+    }
+
+    /*! If the key is in the map, the function returns the value associated with the key, otherwise it returns the default value. */
+    __forceinline Val lookup(const Key& key, const Val& def) const 
+    {
+      typename std::vector<KeyValue>::const_iterator i = std::lower_bound(vec.begin(), vec.end(), key);
+      if (i == vec.end()) return def;
+      if (i->key != key) return def;
+      return i->val;
+    }
+
+    /*! clears all state */
+    void clear() {
+      vec.clear();
+    }
+
+  private:
+    std::vector<KeyValue> vec;    //!< vector containing sorted elements
+  };
+}

Some files were not shown because too many files changed in this diff