Просмотр исходного кода

Merge branch 'TorqueGameEngines:development' into development

marauder2k7 1 год назад
Родитель
Сommit
93418b5d35
42 измененных файлов с 1415 добавлено и 220 удалено
  1. 8 6
      Engine/source/CMakeLists.txt
  2. 1 1
      Engine/source/T3D/spotLight.cpp
  3. 22 0
      Engine/source/console/arrayObject.cpp
  4. 5 1
      Engine/source/console/arrayObject.h
  5. 20 0
      Engine/source/core/util/tDictionary.h
  6. 8 3
      Engine/source/gfx/bitmap/gBitmap.cpp
  7. 111 7
      Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp
  8. 581 0
      Engine/source/gfx/bitmap/loaders/ies/ies_loader.cpp
  9. 116 0
      Engine/source/gfx/bitmap/loaders/ies/ies_loader.h
  10. 10 5
      Engine/source/lighting/advanced/advancedLightBinManager.cpp
  11. 2 2
      Engine/source/lighting/advanced/advancedLightBinManager.h
  12. 15 0
      Engine/source/lighting/advanced/advancedLightManager.cpp
  13. 35 2
      Engine/source/lighting/shadowMap/lightShadowMap.cpp
  14. 7 0
      Engine/source/lighting/shadowMap/lightShadowMap.h
  15. 2 1
      Engine/source/materials/materialDefinition.h
  16. 9 0
      Engine/source/materials/processedCustomMaterial.cpp
  17. 1 0
      Engine/source/platform/types.h
  18. 1 0
      Engine/source/platform/typesLinux.h
  19. 1 0
      Engine/source/platform/typesWin32.h
  20. 1 0
      Engine/source/platform/typesX86UNIX.h
  21. 1 5
      Engine/source/scene/sceneObject.cpp
  22. 42 34
      Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp
  23. 14 12
      Engine/source/ts/assimp/assimpAppNode.cpp
  24. 47 44
      Engine/source/ts/assimp/assimpAppSequence.cpp
  25. 1 1
      Engine/source/ts/assimp/assimpAppSequence.h
  26. 58 9
      Engine/source/ts/assimp/assimpShapeLoader.cpp
  27. 40 0
      LICENSE.md
  28. 5 2
      Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript
  29. 24 1
      Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/pointLightP.glsl
  30. 33 15
      Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/spotLightP.glsl
  31. 37 16
      Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/pointLightP.hlsl
  32. 30 13
      Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/spotLightP.hlsl
  33. 1 1
      Templates/BaseGame/game/core/utility/scripts/helperFunctions.tscript
  34. 80 0
      Templates/BaseGame/game/data/Prototyping/iesprofiles/jelly-fish.ies
  35. 16 0
      Templates/BaseGame/game/data/Prototyping/iesprofiles/top-post.ies
  36. 13 0
      Templates/BaseGame/game/data/Prototyping/iesprofiles/x-arrow.ies
  37. 0 6
      Templates/BaseGame/game/data/Prototyping/shapes/kork_chanShape.asset.taml
  38. 5 25
      Templates/BaseGame/game/data/Prototyping/shapes/kork_chanShape.tscript
  39. 5 0
      Templates/BaseGame/game/data/Prototyping/shapes/kork_chanShape_shape.asset.taml
  40. 1 2
      Templates/BaseGame/game/data/Prototyping/shapes/kork_chan_image.asset.taml
  41. 3 4
      Templates/BaseGame/game/data/Prototyping/shapes/kork_chan_mat.asset.taml
  42. 3 2
      Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.tscript

+ 8 - 6
Engine/source/CMakeLists.txt

@@ -58,10 +58,12 @@ torqueAddSourceDirectories("platform" "platform/threads" "platform/async"
 
 torqueAddSourceDirectories("platform/nativeDialogs")
 # Handle T3D
-torqueAddSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics"
-                              "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret"
-                              "T3D/lighting" "T3D/gameOBjects" "T3D/components"
-                              "T3D/systems" "T3D/assets" "T3D" "T3D/gameBase/std")
+torqueAddSourceDirectories( "T3D" "T3D/assets" "T3D/decal" "T3D/examples" "T3D/fps" "T3D/fx" 
+                           "T3D/gameBase" "T3D/gameBase/std"
+                           "T3D/lighting"
+                           "T3D/physics"
+#                          "T3D/components" "T3D/sceneComponent" "T3D/systems" "T3D/gameOBjects" 
+                           "T3D/sfx" "T3D/turret" "T3D/vehicles")
 
 # Handle TS
 torqueAddSourceDirectories("ts" "ts/collada" "ts/assimp" "ts/loader" "ts/arch")
@@ -79,7 +81,7 @@ if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED)
     endif()
 endif()
 # Handle GFX
-torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders"
+torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders" "gfx/bitmap/loaders/ies"
                              "gfx/util" "gfx/video" "gfx/sim" )
 
 # add the stb headers
@@ -95,7 +97,7 @@ endif (WIN32 AND TORQUE_D3D11)
 
 # Handle core
 torqueAddSourceDirectories("core" "core/stream" "core/strings" "core/util"
-                              "core/util/journal" "core/util/zip" "core/util/compressors")
+                              "core/util/journal" "core/util/zip" "core/util/zip/compressors")
 # Handle GUI
 torqueAddSourceDirectories("gui" "gui/buttons" "gui/containers" "gui/controls" "gui/core"
                               "gui/game" "gui/shiny" "gui/utility" "gui/3d")

+ 1 - 1
Engine/source/T3D/spotLight.cpp

@@ -132,7 +132,7 @@ void SpotLight::_conformLights()
    mLight->setDynamicRefreshFreq(mDynamicRefreshFreq);
    mLight->setPriority( mPriority );
 
-   mOuterConeAngle = getMax( 0.01f, mOuterConeAngle );
+   mOuterConeAngle = getMin(getMax( 0.01f, mOuterConeAngle ),179.0f);
    mInnerConeAngle = getMin( mInnerConeAngle, mOuterConeAngle );
 
    mLight->setInnerConeAngle( mInnerConeAngle );

+ 22 - 0
Engine/source/console/arrayObject.cpp

@@ -404,6 +404,22 @@ void ArrayObject::uniqueKey()
 
 //-----------------------------------------------------------------------------
 
+void ArrayObject::uniquePair()
+{
+   for (S32 i = 0; i < mArray.size(); i++)
+   {
+      for (S32 j = i + 1; j < mArray.size(); j++)
+      {
+         if (isEqual(mArray[i].key, mArray[j].key) && isEqual(mArray[i].value, mArray[j].value))
+         {
+            erase(j);
+            j--;
+         }
+      }
+   }
+}
+//-----------------------------------------------------------------------------
+
 void ArrayObject::duplicate(ArrayObject* obj)
 {
    empty();
@@ -740,6 +756,12 @@ DefineEngineMethod( ArrayObject, uniqueKey, void, (),,
    object->uniqueKey();
 }
 
+DefineEngineMethod(ArrayObject, uniquePair, void, (), ,
+   "Removes any elements that have duplicated key and value pairs (leaving the first instance)")
+{
+   object->uniquePair();
+}
+
 DefineEngineMethod( ArrayObject, duplicate, bool, ( ArrayObject* target ),,
    "Alters array into an exact duplicate of the target array.\n"
    "@param target ArrayObject to duplicate\n" )

+ 5 - 1
Engine/source/console/arrayObject.h

@@ -158,6 +158,10 @@ public:
    /// (keeps the first instance only)
    void uniqueKey();
 
+   /// Removes any duplicate keys from the array
+   /// (keeps the first instance only)
+   void uniquePair();
+
    /// Makes this array an exact duplicate of another array
    void duplicate( ArrayObject *obj );
 
@@ -229,4 +233,4 @@ public:
    static void initPersistFields();
 };
 
-#endif // _ARRAYOBJECT_H_
+#endif // _ARRAYOBJECT_H_

+ 20 - 0
Engine/source/core/util/tDictionary.h

@@ -65,6 +65,21 @@ struct CompoundKey3
    bool operator==(const CompoundKey3 & compound) const { return key1==compound.key1 && key2==compound.key2 && key3==compound.key3; }
 };
 
+template<class A, class B, class C, class D>
+struct CompoundKey4
+{
+   A key1;
+   B key2;
+   C key3;
+   D key4;
+
+   CompoundKey4() {};
+   CompoundKey4(const A& a, const B& b, const C& c, const D& d) { key1 = a; key2 = b; key3 = c; key4 = d;};
+
+   bool operator==(const CompoundKey4& compound) const { return key1 == compound.key1 && key2 == compound.key2 && key3 == compound.key3 && key4 == compound.key4; }
+};
+
+
 
 namespace DictHash
 {
@@ -110,6 +125,11 @@ namespace DictHash
       return hash(compound.key1) + hash(compound.key2) + hash(compound.key3);
    }
 
+   template<class A, class B, class C, class D>
+   inline U32 hash(const CompoundKey4<A, B, C, D>& compound)
+   {
+      return hash(compound.key1) + hash(compound.key2) + hash(compound.key3) + hash(compound.key4);
+   }
    U32 nextPrime(U32);
 };
 

+ 8 - 3
Engine/source/gfx/bitmap/gBitmap.cpp

@@ -1279,9 +1279,14 @@ template<> void *Resource<GBitmap>::create(const Torque::Path &path)
    const String extension = path.getExtension();
    if( !bmp->readBitmap( extension, path ) )
    {
-      Con::errorf( "Resource<GBitmap>::create - error reading '%s'", path.getFullPath().c_str() );
-      delete bmp;
-      bmp = NULL;
+      // we can only get here if the stream was successful, so attempt to read the stream.
+      Con::warnf("Was unable to load as file, going to try the stream instead.");
+      if (!bmp->readBitmapStream(extension, stream, stream.getStreamSize()))
+      {
+         Con::errorf("Resource<GBitmap>::create - error reading '%s'", path.getFullPath().c_str());
+         delete bmp;
+         bmp = NULL;
+      }
    }
 
    return bmp;

+ 111 - 7
Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp

@@ -28,6 +28,7 @@
 #include "core/strings/stringFunctions.h"
 #include "gfx/bitmap/gBitmap.h"
 #include "gfx/bitmap/imageUtils.h"
+#include "gfx/bitmap/loaders/ies/ies_loader.h"
 
 #ifdef __clang__
 #define STBIWDEF static inline
@@ -69,6 +70,7 @@ static struct _privateRegisterSTB
       reg.extensions.push_back("psd");
       reg.extensions.push_back("hdr");
       reg.extensions.push_back("tga");
+      reg.extensions.push_back("ies");
 
       reg.readFunc = sReadSTB;
       reg.readStreamFunc = sReadStreamSTB;
@@ -93,10 +95,112 @@ bool sReadSTB(const Torque::Path& path, GBitmap* bitmap)
 
    U32 prevWaterMark = FrameAllocator::getWaterMark();
 
+   // if this is an ies profile we need to create a texture for it.
+   if (ext.equal("ies"))
+   {
+      String textureName = path.getFullPath();
+      textureName.replace(".ies", ".png");
+      x = 256;
+      y = 1;
+      n = 4;
+      channels = 4;
+      GFXFormat format = GFXFormatR8G8B8A8;
+
+      if (Torque::FS::IsFile(textureName.c_str()))
+      {
+         // if the txture already exist, load it.
+         unsigned char* data = stbi_load(textureName.c_str(), &x, &y, &n, channels);
+
+         // actually allocate the bitmap space...
+         bitmap->allocateBitmap(x, y,
+            false,            // don't extrude miplevels...
+            format);          // use determined format...
+
+         U8* pBase = (U8*)bitmap->getBits();
+
+         U32 rowBytes = bitmap->getByteSize();
+
+         dMemcpy(pBase, data, rowBytes);
+
+         stbi_image_free(data);
+
+         FrameAllocator::setWaterMark(prevWaterMark);
+
+         return true;
+      }
+      else
+      {
+         FileStream* readIes = new FileStream;
+
+         if (!readIes->open(path.getFullPath(), Torque::FS::File::Read))
+         {
+            Con::printf("Failed to open IES profile:%s", path.getFullFileName().c_str());
+            return false;
+         }
+
+         if (readIes->getStatus() != Stream::Ok)
+         {
+            Con::printf("Failed to open IES profile:%s", path.getFullFileName().c_str());
+            return false;
+         }
+
+         U32 buffSize = readIes->getStreamSize();
+         char* buffer = new char[buffSize];
+         readIes->read(buffSize, buffer);
+         
+
+         IESFileInfo info;
+         IESLoadHelper IESLoader;
+
+         if (!IESLoader.load(buffer, buffSize, info))
+         {
+            Con::printf("Failed to load IES profile:%s \n LoaderError: %s", path.getFullFileName().c_str(), info.error().c_str());
+            return false;
+         }
+
+         float* data = new float[x*y*channels];
+
+         if (!IESLoader.saveAs1D(info, data, x, channels))
+         {
+            Con::printf("Failed to create 2d Texture for IES profile:%s", path.getFullFileName().c_str());
+            return false;
+         }
+
+         // use stb function to convert float data to uchar
+         unsigned char* dataChar = stbi__hdr_to_ldr(data, x, y, channels);
+
+         bitmap->deleteImage();
+         // actually allocate the bitmap space...
+         bitmap->allocateBitmap(x, y,
+            false,
+            format);
+
+         U8* pBase = (U8*)bitmap->getBits();
+
+         U32 rowBytes = x * y * channels;
+
+         dMemcpy(pBase, dataChar, rowBytes);
+
+         stbi_image_free(dataChar);
+
+         FrameAllocator::setWaterMark(prevWaterMark);
+
+         sWriteSTB(textureName, bitmap, 10);
+
+         return true;
+      }
+
+   }
+
    if (!stbi_info(path.getFullPath().c_str(), &x, &y, &channels))
    {
       FrameAllocator::setWaterMark(prevWaterMark);
-      return false;
+      const char* stbErr = stbi_failure_reason();
+
+      if (!stbErr)
+         stbErr = "Unknown Error!";
+
+      Con::errorf("STB get file info: %s", stbErr);
    }
 
    // do this to map 2 channels to 4, 2 channel not supported by gbitmap yet..
@@ -142,21 +246,22 @@ bool sReadSTB(const Torque::Path& path, GBitmap* bitmap)
    if (ext.equal("hdr"))
    {
       // force load to 4 channel.
-      float* data = stbi_loadf(path.getFullPath().c_str(), &x, &y, &n, 4);
+      float* data = stbi_loadf(path.getFullPath().c_str(), &x, &y, &n, 0);
 
-      unsigned char* dataChar = stbi__hdr_to_ldr(data, x, y, 4);
+      unsigned char* dataChar = stbi__hdr_to_ldr(data, x, y, n);
       bitmap->deleteImage();
       // actually allocate the bitmap space...
       bitmap->allocateBitmap(x, y,
          false,
-         GFXFormatR8G8B8A8);
+         GFXFormatR8G8B8);
 
       U8* pBase = (U8*)bitmap->getBits();
 
-      U32 rowBytes = x * y * 4;
+      U32 rowBytes = x * y * n;
 
       dMemcpy(pBase, dataChar, rowBytes);
 
+      //stbi_image_free(data);
       stbi_image_free(dataChar);
 
       FrameAllocator::setWaterMark(prevWaterMark);
@@ -225,8 +330,7 @@ bool sReadStreamSTB(Stream& stream, GBitmap* bitmap, U32 len)
       if (!stbErr)
          stbErr = "Unknown Error!";
 
-      Con::errorf("STB failed to get image info: %s", stbErr);
-      return false;
+      Con::errorf("STB get memory info: %s", stbErr);
    }
 
    S32 reqCom = comp;

+ 581 - 0
Engine/source/gfx/bitmap/loaders/ies/ies_loader.cpp

@@ -0,0 +1,581 @@
+// +----------------------------------------------------------------------
+// | Project : ray.
+// | All rights reserved.
+// +----------------------------------------------------------------------
+// | Copyright (c) 2013-2017.
+// +----------------------------------------------------------------------
+// | * Redistribution and use of this software in source and binary forms,
+// |   with or without modification, are permitted provided that the following
+// |   conditions are met:
+// |
+// | * Redistributions of source code must retain the above
+// |   copyright notice, this list of conditions and the
+// |   following disclaimer.
+// |
+// | * Redistributions in binary form must reproduce the above
+// |   copyright notice, this list of conditions and the
+// |   following disclaimer in the documentation and/or other
+// |   materials provided with the distribution.
+// |
+// | * Neither the name of the ray team, nor the names of its
+// |   contributors may be used to endorse or promote products
+// |   derived from this software without specific prior
+// |   written permission of the ray team.
+// |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// +----------------------------------------------------------------------
+#include "ies_loader.h"
+#include <assert.h>
+#include <algorithm>
+#include <functional>
+
+#include "math/mMathFn.h"
+
+IESFileInfo::IESFileInfo()
+	: _cachedIntegral(F32_MAX)
+	, _error("No data loaded")
+{
+}
+
+bool
+IESFileInfo::valid() const
+{
+	return _error.empty();
+}
+
+const std::string&
+IESFileInfo::error() const
+{
+	return _error;
+}
+
+IESLoadHelper::IESLoadHelper()
+{
+}
+
+IESLoadHelper::~IESLoadHelper()
+{
+}
+
+bool
+IESLoadHelper::load(const char* data, std::size_t dataLength, IESFileInfo& info)
+{
+	assert(!info.valid());
+	return this->load(std::string(data, dataLength), info);
+}
+
+bool
+IESLoadHelper::load(const std::string& data, IESFileInfo& info)
+{
+	assert(!info.valid());
+
+	std::string dataPos;
+
+	std::string version;
+	this->getLineContent(data, dataPos, version, false, false);
+
+	if (version.empty())
+	{
+		info._error = "Unknown IES version";
+		return false;
+	}
+	else if (version == "IESNA:LM-63-1995")
+		info._version = "IESNA:LM-63-1995";
+	else if (version == "IESNA91")
+		info._version = "IESNA91";
+	else if (version == "IESNA:LM-63-2002")
+		info._version = "IESNA:LM-63-2002";
+	else
+		info._version = version;
+
+	while (!dataPos.empty())
+	{
+		std::string line;
+		this->getLineContent(dataPos, dataPos, line, false, false);
+
+		if (line.compare(0, 9, "TILT=NONE", 9) == 0 ||
+			line.compare(0, 10, "TILT= NONE", 10) == 0 ||
+			line.compare(0, 10, "TILT =NONE", 10) == 0 ||
+			line.compare(0, 11, "TILT = NONE", 11) == 0)
+		{
+			break;
+		}
+		else if (line.compare(0, 5, "TILT=", 5) == 0 ||
+			line.compare(0, 5, "TILT =", 5) == 0)
+		{
+			info._error = "Not supported yet.";
+			return false;
+		}
+	}
+
+	this->getFloat(dataPos, dataPos, info.totalLights);
+	if (info.totalLights < 0 || info.totalLights > F32_MAX)
+	{
+		info._error = "Light Count is not valid";
+		return false;
+	}
+
+	this->getFloat(dataPos, dataPos, info.totalLumens);
+	if (info.totalLumens < 0)
+	{
+		info._error = "TotalLumens is not positive number";
+		return false;
+	}
+
+	this->getFloat(dataPos, dataPos, info.candalaMult);
+	if (info.candalaMult < 0)
+	{
+		info._error = "CandalaMult is not positive number";
+		return false;
+	}
+
+	this->getInt(dataPos, dataPos, info.anglesNumV);
+   if (info.anglesNumV < 0 || info.anglesNumV > F32_MAX)
+	{
+		info._error = "VAnglesNum is not valid";
+		return false;
+	}
+
+	this->getInt(dataPos, dataPos, info.anglesNumH);
+	if (info.anglesNumH < 0 || info.anglesNumH > F32_MAX)
+	{
+		info._error = "HAnglesNum is not valid";
+		return false;
+	}
+
+	this->getInt(dataPos, dataPos, info.typeOfPhotometric);
+	this->getInt(dataPos, dataPos, info.typeOfUnit);
+
+	this->getFloat(dataPos, dataPos, info.width);
+	this->getFloat(dataPos, dataPos, info.length);
+	this->getFloat(dataPos, dataPos, info.height);
+
+	this->getFloat(dataPos, dataPos, info.ballastFactor);
+	this->getFloat(dataPos, dataPos, info.futureUse);
+	this->getFloat(dataPos, dataPos, info.inputWatts);
+
+   float minSoFarV = F32_MIN_EX;
+   float minSoFarH = F32_MIN_EX;
+
+	info._anglesV.reserve(info.anglesNumV);
+	info._anglesH.reserve(info.anglesNumH);
+
+	for (std::int32_t y = 0; y < info.anglesNumV; ++y)
+	{
+		float value;
+		this->getFloat(dataPos, dataPos, value, true, true);
+
+		if (value < minSoFarV)
+		{
+			info._error = "V Values is not valid";
+			return false;
+		}
+
+		minSoFarV = value;
+		info._anglesV.push_back(value);
+	}
+
+	for (std::int32_t x = 0; x < info.anglesNumH; ++x)
+	{
+		float value;
+		this->getFloat(dataPos, dataPos, value, true, true);
+
+		if (value < minSoFarH)
+		{
+			info._error = "H Values is not valid";
+			return false;
+		}
+
+		minSoFarH = value;
+		info._anglesH.push_back(value);
+	}
+
+	info._candalaValues.reserve(info.anglesNumH * info.anglesNumV);
+
+	for (std::int32_t y = 0; y < info.anglesNumH; ++y)
+	{
+		for (std::int32_t x = 0; x < info.anglesNumV; ++x)
+		{
+			float value;
+			this->getFloat(dataPos, dataPos, value, true, true);
+			info._candalaValues.push_back(value * info.candalaMult);
+		}
+	}
+
+	skipSpaceAndLineEnd(dataPos, dataPos);
+
+	if (!dataPos.empty())
+	{
+		std::string line;
+		this->getLineContent(dataPos, dataPos, line, true, false);
+
+		if (line == "END")
+			skipSpaceAndLineEnd(dataPos, dataPos);
+
+		if (!dataPos.empty())
+		{
+			info._error = "Unexpected content after END.";
+			return false;
+		}
+	}
+
+	info._error.clear();
+
+	return true;
+}
+
+bool
+IESLoadHelper::saveAs1D(const IESFileInfo& info, float* data, std::uint32_t width, std::uint8_t channel) noexcept
+{
+	assert(data);
+	assert(width > 0);
+	assert(channel == 1 || channel == 3 || channel == 4);
+	assert(info.valid());
+
+	float invW = 1.0f / width;
+	float invMaxValue = this->computeInvMax(info._candalaValues);
+
+	for (std::uint32_t x = 0; x < width; ++x)
+	{
+		float fraction = x * invW;
+		float value = invMaxValue * interpolate1D(info, fraction * 180.0f);
+
+		switch (channel)
+		{
+		case 1:
+			*data++ = value;
+			break;
+		case 3:
+			*data++ = value;
+			*data++ = value;
+			*data++ = value;
+			break;
+		case 4:
+			*data++ = value;
+			*data++ = value;
+			*data++ = value;
+			*data++ = 1.0f;
+			break;
+		default:
+			return false;
+		}
+	}
+
+	return true;
+}
+
+bool
+IESLoadHelper::saveAs2D(const IESFileInfo& info, float* data, std::uint32_t width, std::uint32_t height, std::uint8_t channel) noexcept
+{
+	assert(data);
+	assert(width > 0 && height > 0);
+	assert(channel == 1 || channel == 3 || channel == 4);
+	assert(info.valid());
+
+	float invW = 1.0f / width;
+	float invH = 1.0f / height;
+	float invMaxValue = this->computeInvMax(info._candalaValues);
+
+	for (std::uint32_t y = 0; y < height; ++y)
+	{
+		for (std::uint32_t x = 0; x < width; ++x)
+		{
+			float fractionV = x * invW * 180.0f;
+			float fractionH = y * invH * 180.0f;
+			float value = invMaxValue * interpolate2D(info, fractionV, fractionH);
+
+			switch (channel)
+			{
+			case 1:
+				*data++ = value;
+				break;
+			case 3:
+				*data++ = value;
+				*data++ = value;
+				*data++ = value;
+				break;
+			case 4:
+				*data++ = value;
+				*data++ = value;
+				*data++ = value;
+				*data++ = 1.0;
+				break;
+			default:
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+bool
+IESLoadHelper::saveAsPreview(const IESFileInfo& info, std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint8_t channel) noexcept
+{
+	assert(data);
+	assert(width > 0 && height > 0);
+	assert(channel == 1 || channel == 3 || channel == 4);
+	assert(info.valid());
+
+	std::vector<float> ies(256);
+	if (!this->saveAs1D(info, ies.data(), ies.size(), 1))
+		return false;
+
+	float maxValue = this->computeInvMax(info._candalaValues);
+
+	auto TonemapHable = [](float x)
+	{
+		const float A = 0.22f;
+		const float B = 0.30f;
+		const float C = 0.10f;
+		const float D = 0.20f;
+		const float E = 0.01f;
+		const float F = 0.30f;
+		return ((x*(A*x + C*B) + D*E) / (x*(A*x + B) + D*F)) - E / F;
+	};
+
+	for (int y = 0; y < height; y++)
+	{
+		for (int x = 0; x < width; x++)
+		{
+			float u = ((float)x / width) * 2.0f - 1.0f;
+			float v = 1.0f - ((float)y / height) * 2.0f - 1.0f;
+
+			u *= 2.2f;
+			v *= 2.4f;
+
+			// float3(0.0f, 0.0f, -0.5f) - ray::float3(u, v, 0.0f)
+			float lx = +0.0f - u;
+			float ly = +0.0f - v;
+			float lz = -0.5f - 0.0f;
+
+			// normalize
+			float length = mSqrt(lx * lx + ly * ly + lz * lz);
+			lx /= length;
+			ly /= length;
+			lz /= length;
+
+			float angle = 1.0 - mAcos(lx * 0.0 + ly * -1.0 + lz * 0.0f) / 3.141592654;
+
+			float intensity = ies[angle * 255] * maxValue / length;
+
+			std::uint8_t value = std::min(std::max((int)mFloor(TonemapHable(intensity) / TonemapHable(maxValue) * 255.0f), 0), 255);
+
+			switch (channel)
+			{
+			case 1:
+				*data++ = value;
+				break;
+			case 3:
+				*data++ = value;
+				*data++ = value;
+				*data++ = value;
+				break;
+			case 4:
+				*data++ = value;
+				*data++ = value;
+				*data++ = value;
+				*data++ = 1.0;
+				break;
+			default:
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+float
+IESLoadHelper::computeInvMax(const std::vector<float>& candalaValues) const
+{
+	assert(candalaValues.size());
+
+	float candala = *std::max_element(candalaValues.begin(), candalaValues.end());
+	return 1.0f / candala;
+}
+
+float
+IESLoadHelper::computeFilterPos(float value, const std::vector<float>& angles) const
+{
+	assert(angles.size());
+
+	std::size_t start = 0;
+	std::size_t end = angles.size() - 1;
+
+	if (value < angles[start]) return 0.0f;
+	if (value > angles[end]) return (float)end;
+
+	while (start < end)
+	{
+		std::size_t index = (start + end + 1) / 2;
+
+		float angle = angles[index];
+		if (value >= angle)
+		{
+			assert(start != index);
+			start = index;
+		}
+		else
+		{
+			assert(end != index - 1);
+			end = index - 1;
+		}
+	}
+
+	float leftValue = angles[start];
+	float fraction = 0.0f;
+
+	if (start + 1 < (std::uint32_t)angles.size())
+	{
+		float rightValue = angles[start + 1];
+		float deltaValue = rightValue - leftValue;
+
+		if (deltaValue > 0.0001f)
+		{
+			fraction = (value - leftValue) / deltaValue;
+		}
+	}
+
+	return start + fraction;
+}
+
+float
+IESLoadHelper::interpolate1D(const IESFileInfo& info, float angle) const
+{
+	float angleV = this->computeFilterPos(angle, info._anglesV);
+	float anglesNum = (float)info._anglesH.size();
+	float angleTotal = 0.0f;
+
+	for (float x = 0; x < anglesNum; x++)
+		angleTotal += this->interpolateBilinear(info, x, angleV);
+
+	return angleTotal / anglesNum;
+}
+
+float
+IESLoadHelper::interpolate2D(const IESFileInfo& info, float angleV, float angleH) const
+{
+	float u = this->computeFilterPos(angleH, info._anglesH);
+	float v = this->computeFilterPos(angleV, info._anglesV);
+	return this->interpolateBilinear(info, u, v);
+}
+
+float
+IESLoadHelper::interpolatePoint(const IESFileInfo& info, std::uint32_t x, std::uint32_t y) const
+{
+	assert(x >= 0);
+	assert(y >= 0);
+
+	std::size_t anglesNumH = info._anglesH.size();
+	std::size_t anglesNumV = info._anglesV.size();
+
+	x %= anglesNumH;
+	y %= anglesNumV;
+
+	assert(x < anglesNumH);
+	assert(y < anglesNumV);
+
+	return info._candalaValues[y + anglesNumV * x];
+}
+
+float
+IESLoadHelper::interpolateBilinear(const IESFileInfo& info, float x, float y) const
+{
+	int ix = (int)mFloor(x);
+	int iy = (int)mFloor(y);
+
+	float fracX = x - ix;
+	float fracY = y - iy;
+
+	float p00 = this->interpolatePoint(info, ix + 0, iy + 0);
+	float p10 = this->interpolatePoint(info, ix + 1, iy + 0);
+	float p01 = this->interpolatePoint(info, ix + 0, iy + 1);
+	float p11 = this->interpolatePoint(info, ix + 1, iy + 1);
+
+	auto lerp = [](float t1, float t2, float t3) -> float { return t1 + (t2 - t1) * t3; };
+
+	float p0 = lerp(p00, p01, fracY);
+	float p1 = lerp(p10, p11, fracY);
+
+	return lerp(p0, p1, fracX);
+}
+
+void
+IESLoadHelper::skipSpaceAndLineEnd(const std::string& data, std::string& out, bool stopOnComma)
+{
+	std::size_t dataBegin = 0;
+	std::size_t dataEnd = data.size();
+
+	while (dataBegin < dataEnd)
+	{
+		if (data[dataBegin] != '\r' && data[dataBegin] != '\n' && data[dataBegin] > ' ')
+			break;
+		dataBegin++;
+	}
+
+	if (stopOnComma)
+	{
+		while (dataBegin < dataEnd)
+		{
+			if (data[dataBegin] != ',')
+				break;
+			dataBegin++;
+		}
+	}
+
+	out = data.substr(dataBegin, data.size() - dataBegin);
+}
+
+void
+IESLoadHelper::getLineContent(const std::string& data, std::string& next, std::string& line, bool stopOnWhiteSpace, bool stopOnComma)
+{
+	skipSpaceAndLineEnd(data, next);
+
+	auto it = data.begin();
+	auto end = data.end();
+
+	for (; it < end; ++it)
+	{
+		if ((*it == '\r') ||
+			(*it == '\n') ||
+			(*it <= ' ' && stopOnWhiteSpace) ||
+			(*it == ',' && stopOnComma))
+		{
+			break;
+		}
+	}
+
+	line.assign(data, 0, it - data.begin());
+	next.assign(data, it - data.begin(), end - it);
+
+	skipSpaceAndLineEnd(next, next, stopOnComma);
+}
+
+void
+IESLoadHelper::getFloat(const std::string& data, std::string& next, float& ret, bool stopOnWhiteSpace, bool stopOnComma)
+{
+	std::string line;
+	getLineContent(data, next, line, stopOnWhiteSpace, stopOnComma);
+	assert(!line.empty());
+	ret = (float)std::atof(line.c_str());
+}
+
+void
+IESLoadHelper::getInt(const std::string& data, std::string& next, std::int32_t& ret, bool stopOnWhiteSpace, bool stopOnComma)
+{
+	std::string line;
+	getLineContent(data, next, line, stopOnWhiteSpace, stopOnComma);
+	assert(!line.empty());
+	ret = std::atoi(line.c_str());
+}

+ 116 - 0
Engine/source/gfx/bitmap/loaders/ies/ies_loader.h

@@ -0,0 +1,116 @@
+// +----------------------------------------------------------------------
+// | Project : ray.
+// | All rights reserved.
+// +----------------------------------------------------------------------
+// | Copyright (c) 2013-2017.
+// +----------------------------------------------------------------------
+// | * Redistribution and use of this software in source and binary forms,
+// |   with or without modification, are permitted provided that the following
+// |   conditions are met:
+// |
+// | * Redistributions of source code must retain the above
+// |   copyright notice, this list of conditions and the
+// |   following disclaimer.
+// |
+// | * Redistributions in binary form must reproduce the above
+// |   copyright notice, this list of conditions and the
+// |   following disclaimer in the documentation and/or other
+// |   materials provided with the distribution.
+// |
+// | * Neither the name of the ray team, nor the names of its
+// |   contributors may be used to endorse or promote products
+// |   derived from this software without specific prior
+// |   written permission of the ray team.
+// |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// +----------------------------------------------------------------------
+#ifndef _H_IES_LOADER_H_
+#define _H_IES_LOADER_H_
+
+#include <vector>
+#include <string>
+
+// https://knowledge.autodesk.com/support/3ds-max/learn-explore/caas/CloudHelp/cloudhelp/2016/ENU/3DSMax/files/GUID-EA0E3DE0-275C-42F7-83EC-429A37B2D501-htm.html
+class IESFileInfo
+{
+public:
+	IESFileInfo();
+
+	bool valid() const;
+
+	const std::string& error() const;
+
+public:
+	float totalLights;
+	float totalLumens;
+
+	float candalaMult;
+
+	std::int32_t typeOfPhotometric;
+	std::int32_t typeOfUnit;
+
+	std::int32_t anglesNumH;
+	std::int32_t anglesNumV;
+
+	float width;
+	float length;
+	float height;
+
+	float ballastFactor;
+	float futureUse;
+	float inputWatts;
+
+private:
+	friend class IESLoadHelper;
+
+	float _cachedIntegral;
+
+	std::string _error;
+	std::string _version;
+
+	std::vector<float> _anglesH;
+	std::vector<float> _anglesV;
+	std::vector<float> _candalaValues;
+};
+
+class IESLoadHelper final
+{
+public:
+	IESLoadHelper();
+	~IESLoadHelper();
+
+	bool load(const std::string& data, IESFileInfo& info);
+	bool load(const char* data, std::size_t dataLength, IESFileInfo& info);
+
+	bool saveAs1D(const IESFileInfo& info, float* data, std::uint32_t width = 256, std::uint8_t channel = 3) noexcept;
+	bool saveAs2D(const IESFileInfo& info, float* data, std::uint32_t width = 256, std::uint32_t height = 256, std::uint8_t channel = 3) noexcept;
+	bool saveAsPreview(const IESFileInfo& info, std::uint8_t* data, std::uint32_t width = 64, std::uint32_t height = 64, std::uint8_t channel = 3) noexcept;
+
+private:
+	float computeInvMax(const std::vector<float>& candalaValues) const;
+	float computeFilterPos(float value, const std::vector<float>& angle) const;
+
+	float interpolate1D(const IESFileInfo& info, float angle) const;
+	float interpolate2D(const IESFileInfo& info, float angleV, float angleH) const;
+	float interpolatePoint(const IESFileInfo& info, std::uint32_t x, std::uint32_t y) const;
+	float interpolateBilinear(const IESFileInfo& info, float x, float y) const;
+
+private:
+	static void skipSpaceAndLineEnd(const std::string& data, std::string& out, bool stopOnComma = false);
+
+	static void getLineContent(const std::string& data, std::string& next, std::string& line, bool stopOnWhiteSpace, bool stopOnComma);
+	static void getFloat(const std::string& data, std::string& next, float& ret, bool stopOnWhiteSpace = true, bool stopOnComma = false);
+	static void getInt(const std::string& data, std::string& next, std::int32_t& ret, bool stopOnWhiteSpace = true, bool stopOnComma = false);
+};
+
+#endif

+ 10 - 5
Engine/source/lighting/advanced/advancedLightBinManager.cpp

@@ -253,7 +253,7 @@ void AdvancedLightBinManager::addLight( LightInfo *light )
    LightBinEntry lEntry;
    lEntry.lightInfo = light;
    lEntry.shadowMap = lsm;
-   lEntry.lightMaterial = _getLightMaterial( lightType, shadowType, lsp->hasCookieTex() );
+   lEntry.lightMaterial = _getLightMaterial( lightType, shadowType, lsp->hasCookieTex(), lsp->hasIesProfile() );
 
    if( lightType == LightInfo::Spot )
       lEntry.vertBuffer = mLightManager->getConeMesh( lEntry.numPrims, lEntry.primBuffer );
@@ -399,9 +399,9 @@ void AdvancedLightBinManager::render( SceneRenderState *state )
          sunLight->getCastShadows() &&
          !disableShadows &&
          sunLight->getExtended<ShadowMapParams>() )
-      vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_PSSM, false );
+      vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_PSSM,false,false );
    else
-      vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_None, false );
+      vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_None, false, false);
 
    // Initialize and set the per-frame parameters after getting
    // the vector light material as we use lazy creation.
@@ -513,12 +513,13 @@ void AdvancedLightBinManager::render( SceneRenderState *state )
 
 AdvancedLightBinManager::LightMaterialInfo* AdvancedLightBinManager::_getLightMaterial(   LightInfo::Type lightType, 
                                                                                           ShadowType shadowType, 
-                                                                                          bool useCookieTex )
+                                                                                          bool useCookieTex,
+                                                                                          bool isPhotometric)
 {
    PROFILE_SCOPE( AdvancedLightBinManager_GetLightMaterial );
 
    // Build the key.
-   const LightMatKey key( lightType, shadowType, useCookieTex );
+   const LightMatKey key( lightType, shadowType, useCookieTex, isPhotometric );
 
    // See if we've already built this one.
    LightMatTable::Iterator iter = mLightMaterials.find( key );
@@ -558,6 +559,9 @@ AdvancedLightBinManager::LightMaterialInfo* AdvancedLightBinManager::_getLightMa
       if ( useCookieTex )
          shadowMacros.push_back( GFXShaderMacro( "USE_COOKIE_TEX" ) );
 
+      if(isPhotometric)
+         shadowMacros.push_back(GFXShaderMacro("UES_PHOTOMETRIC_MASK"));
+
       // Its safe to add the PSSM debug macro to all the materials.
       if ( smPSSMDebugRender )
          shadowMacros.push_back( GFXShaderMacro( "PSSM_DEBUG_RENDER" ) );
@@ -830,6 +834,7 @@ void AdvancedLightBinManager::LightMaterialInfo::setLightParameters( const Light
          const F32 radius = lightInfo->getRange().x;
          const F32 invSqrRadius = 1.0f / (radius * radius);
          matParams->setSafe( lightRange, radius);
+         matParams->setSafe( lightDirection, -lightInfo->getTransform().getUpVector());
          matParams->setSafe( lightInvSqrRange, invSqrRadius);  
          luxTargMultiplier =radius;
       }

+ 2 - 2
Engine/source/lighting/advanced/advancedLightBinManager.h

@@ -233,14 +233,14 @@ protected:
 
    static const GFXVertexFormat* smLightMatVertex[LightInfo::Count];
 
-   typedef CompoundKey3<LightInfo::Type,ShadowType,bool> LightMatKey;
+   typedef CompoundKey4<LightInfo::Type,ShadowType,bool, bool> LightMatKey;
 
    typedef HashTable<LightMatKey,LightMaterialInfo*> LightMatTable;
 
    /// The fixed table of light material info.
    LightMatTable mLightMaterials;
 
-   LightMaterialInfo* _getLightMaterial( LightInfo::Type lightType, ShadowType shadowType, bool useCookieTex );
+   LightMaterialInfo* _getLightMaterial( LightInfo::Type lightType, ShadowType shadowType, bool useCookieTex = false, bool isPhotometric = false );
 
    ///
    void _onShadowFilterChanged();

+ 15 - 0
Engine/source/lighting/advanced/advancedLightManager.cpp

@@ -277,6 +277,7 @@ void AdvancedLightManager::_initLightFields()
       DEFINE_LIGHT_FIELD( attenuationRatio, TypePoint3F, NULL );
       DEFINE_LIGHT_FIELD( shadowType, TYPEID< ShadowType >(), ConsoleBaseType::getType( TYPEID< ShadowType >() )->getEnumTable() );
       DEFINE_LIGHT_FIELD( texSize, TypeS32, NULL );
+      DEFINE_LIGHT_FIELD( iesProfile, TypeStringFilename, NULL );      
       DEFINE_LIGHT_FIELD( cookie, TypeStringFilename, NULL );      
       DEFINE_LIGHT_FIELD( numSplits, TypeS32, NULL );
       DEFINE_LIGHT_FIELD( logWeight, TypeF32, NULL );
@@ -300,6 +301,9 @@ void AdvancedLightManager::_initLightFields()
       ADD_LIGHT_FIELD( "shadowType", TYPEID< ShadowType >(), shadowType,
          "The type of shadow to use on this light." );
 
+      ADD_LIGHT_FIELD("iesProfile", TypeStringFilename, iesProfile,
+         "A photometric profile for the light.");
+
       ADD_LIGHT_FIELD( "cookie", TypeStringFilename, cookie,
          "A custom pattern texture which is projected from the light." );
 
@@ -496,6 +500,17 @@ bool AdvancedLightManager::setTextureStage(  const SceneData &sgData,
 
       return true;
    }
+   else if (currTexFlag == Material::PhotometricMask)
+   {
+      S32 reg = lsc->mIesProfileSC->getSamplerRegister();
+      if (reg != -1 && sgData.lights[0])
+      {
+         ShadowMapParams* p = sgData.lights[0]->getExtended<ShadowMapParams>();
+         GFX->setTexture(reg, p->getIesProfileTex());
+      }
+
+      return true;
+   }
 
    return false;
 }

+ 35 - 2
Engine/source/lighting/shadowMap/lightShadowMap.cpp

@@ -269,7 +269,8 @@ bool LightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants*
          GFX->setTexture( reg, mShadowMapTex);
 
       return true;
-   } else if ( currTexFlag == Material::DynamicLightMask )
+   }
+   else if ( currTexFlag == Material::DynamicLightMask )
    {
       S32 reg = lsc->mCookieMapSC->getSamplerRegister();
    	if ( reg != -1 )
@@ -284,6 +285,17 @@ bool LightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants*
 
       return true;
    }
+   else if (currTexFlag == Material::PhotometricMask)
+   {
+      S32 reg = lsc->mIesProfileSC->getSamplerRegister();
+      if (reg != -1)
+      {
+         ShadowMapParams* p = mLight->getExtended<ShadowMapParams>();
+         GFX->setTexture(reg, p->getIesProfileTex());
+      }
+
+      return true;
+   }
 
    return false;
 }
@@ -430,6 +442,7 @@ LightingShaderConstants::LightingShaderConstants()
       mShadowMapSC(NULL), 
       mShadowMapSizeSC(NULL), 
       mCookieMapSC(NULL),
+      mIesProfileSC(NULL),
       mRandomDirsConst(NULL),
       mShadowSoftnessConst(NULL), 
       mAtlasXOffsetSC(NULL), 
@@ -490,6 +503,7 @@ void LightingShaderConstants::init(GFXShader* shader)
    mShadowMapSizeSC = shader->getShaderConstHandle("$shadowMapSize");
 
    mCookieMapSC = shader->getShaderConstHandle("$cookieMap");
+   mIesProfileSC = shader->getShaderConstHandle("$iesProfile");
 
    mShadowSoftnessConst = shader->getShaderConstHandle("$shadowSoftness");
    mAtlasXOffsetSC = shader->getShaderConstHandle("$atlasXOffset");
@@ -542,7 +556,8 @@ ShadowMapParams::ShadowMapParams( LightInfo *light )
    fadeStartDist = 75.0f;
    lastSplitTerrainOnly = false;
    mQuery = GFX->createOcclusionQuery();
-   cookie = StringTable->EmptyString();;
+   cookie = StringTable->EmptyString();
+   iesProfile = StringTable->EmptyString();
 
    _validate();
 }
@@ -662,6 +677,22 @@ GFXTextureObject* ShadowMapParams::getCookieTex()
    return mCookieTex.getPointer();
 }
 
+GFXTextureObject* ShadowMapParams::getIesProfileTex()
+{
+   if (hasIesProfile() &&
+      (mIesTex.isNull() ||
+         iesProfile != StringTable->insert(mIesTex->getPath().c_str())))
+   {
+      mIesTex.set(iesProfile,
+         &GFXStaticTextureSRGBProfile,
+         "ShadowMapParams::getIesProfileTex()");
+   }
+   else if (!hasIesProfile())
+      mIesTex = NULL;
+
+   return mIesTex.getPointer();
+}
+
 GFXCubemap* ShadowMapParams::getCookieCubeTex()
 {
    if (  hasCookieTex() &&
@@ -695,6 +726,7 @@ void ShadowMapParams::packUpdate( BitStream *stream ) const
    stream->write( texSize );
 
    stream->writeString( cookie );
+   stream->writeString( iesProfile );
 
    stream->write( numSplits );
    stream->write( logWeight );
@@ -725,6 +757,7 @@ void ShadowMapParams::unpackUpdate( BitStream *stream )
    stream->read( &texSize );
 
    cookie = stream->readSTString();
+   iesProfile = stream->readSTString();
 
    stream->read( &numSplits );
    stream->read( &logWeight );

+ 7 - 0
Engine/source/lighting/shadowMap/lightShadowMap.h

@@ -99,6 +99,7 @@ struct LightingShaderConstants
    GFXShaderConstHandle* mShadowMapSizeSC;
 
    GFXShaderConstHandle* mCookieMapSC;
+   GFXShaderConstHandle* mIesProfileSC;
 
    GFXShaderConstHandle* mRandomDirsConst;
    GFXShaderConstHandle* mShadowSoftnessConst;
@@ -289,11 +290,14 @@ public:
    LightShadowMap* getOrCreateShadowMap();
 
    bool hasCookieTex() const { return cookie != StringTable->EmptyString(); }
+   bool hasIesProfile() const { return iesProfile != StringTable->EmptyString(); }
 
    GFXOcclusionQuery* getOcclusionQuery() const { return mQuery; }
 
    GFXTextureObject* getCookieTex();
 
+   GFXTextureObject* getIesProfileTex();
+
    GFXCubemap* getCookieCubeTex();
 
    // Validates the parameters after a field is changed.
@@ -313,6 +317,8 @@ protected:
 
    GFXCubemapHandle mCookieCubeTex;
 
+   GFXTexHandle mIesTex;
+
 public:
 
    // We're leaving these public for easy access 
@@ -326,6 +332,7 @@ public:
 
    /// 
    StringTableEntry cookie;
+   StringTableEntry iesProfile;
 
    /// @}
 

+ 2 - 1
Engine/source/materials/materialDefinition.h

@@ -71,7 +71,7 @@ public:
    //-----------------------------------------------------------------------
    enum Constants
    {
-      MAX_TEX_PER_PASS = 8,         ///< Number of textures per pass
+      MAX_TEX_PER_PASS = 16,         ///< Number of textures per pass
       MAX_STAGES = 4,
       NUM_EFFECT_COLOR_STAGES = 2,  ///< Number of effect color definitions for transitioning effects.
    };
@@ -94,6 +94,7 @@ public:
       Misc,
       DynamicLight,
       DynamicLightMask,
+      PhotometricMask,
       NormalizeCube,
       TexTarget,
       AccuMap,

+ 9 - 0
Engine/source/materials/processedCustomMaterial.cpp

@@ -94,6 +94,14 @@ void ProcessedCustomMaterial::_setStageData()
          continue;
       }
 
+       if (filename.equal(String("$photometricmask"), String::NoCase))
+       {
+          rpd->mTexType[i] = Material::PhotometricMask;
+          rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i];
+          mMaxTex = i + 1;
+          continue;
+       }
+
       if(filename.equal(String("$lightmap"), String::NoCase))
       {
          rpd->mTexType[i] = Material::Lightmap;
@@ -353,6 +361,7 @@ void ProcessedCustomMaterial::setTextureStages( SceneRenderState *state, const S
             break;
 
          case Material::Mask:
+         case Material::PhotometricMask:
          case Material::Standard:
          case Material::Bump:
          case Material::Detail:

+ 1 - 0
Engine/source/platform/types.h

@@ -100,6 +100,7 @@ static const S32 S32_MIN = S32(-2147483647 - 1);                  ///< Constant
 static const S32 S32_MAX = S32(2147483647);                       ///< Constant Max Limit S32
 static const U32 U32_MAX = U32(0xffffffff);                       ///< Constant Max Limit U32
 
+static const F32 F32_MIN_EX = F32(-3.40282347e+38);               ///< Constant Min Limit F32
 static const F32 F32_MIN = F32(1.175494351e-38F);                 ///< Constant Min Limit F32
 static const F32 F32_MAX = F32(3.402823466e+38F);                 ///< Constant Max Limit F32
 

+ 1 - 0
Engine/source/platform/typesLinux.h

@@ -73,6 +73,7 @@ static const S32 S32_MIN = S32(-2147483647 - 1);
 static const S32 S32_MAX = S32(2147483647);
 static const U32 U32_MAX = U32(0xffffffff);
 
+static const F32 F32_MIN_EX = F32(-3.40282347e+38);
 static const F32 F32_MAX = F32(3.402823466e+38F);
 static const F32 F32_MIN = F32(1.175494351e-38F);
 

+ 1 - 0
Engine/source/platform/typesWin32.h

@@ -116,6 +116,7 @@ static const S32 S32_MIN = S32(-2147483647 - 1);                  ///< Constant
 static const S32 S32_MAX = S32(2147483647);                       ///< Constant Max Limit S32
 static const U32 U32_MAX = U32(0xffffffff);                       ///< Constant Max Limit U32
 
+static const F32 F32_MIN_EX = F32(-3.40282347e+38);               ///< Constant Min Limit F32
 static const F32 F32_MIN = F32(1.175494351e-38F);                 ///< Constant Min Limit F32
 static const F32 F32_MAX = F32(3.402823466e+38F);                 ///< Constant Max Limit F32
 

+ 1 - 0
Engine/source/platform/typesX86UNIX.h

@@ -73,6 +73,7 @@ static const S32 S32_MIN = S32(-2147483647 - 1);
 static const S32 S32_MAX = S32(2147483647);
 static const U32 U32_MAX = U32(0xffffffff);
 
+static const F32 F32_MIN_EX = F32(-3.40282347e+38);
 static const F32 F32_MAX = F32(3.402823466e+38F);
 static const F32 F32_MIN = F32(1.175494351e-38F);
 

+ 1 - 5
Engine/source/scene/sceneObject.cpp

@@ -1729,11 +1729,7 @@ void SceneObject::updateRenderChangesByParent(){
 		//add the "offset" caused by the parents change, and add it to it's own
 		// This is needed by objects that update their own render transform thru interpolate tick
 		// Mostly for stationary objects.
-
-          if (getClassName() == StringTable->insert("Player"))
-			mat.mul(offset,getRenderTransform());  
-		else										
-			mat.mul(offset,getTransform());	 
+      mat.mul(offset,getRenderTransform());
 			setRenderTransform(mat);
 	}
 }

+ 42 - 34
Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp

@@ -438,29 +438,18 @@ Var* ShaderFeatureGLSL::getInColor( const char *name,
 Var* ShaderFeatureGLSL::addOutVpos( MultiLine *meta,
                                     Vector<ShaderComponent*> &componentList )
 {
-   /*
-   // Nothing to do if we're on SM 3.0... we use the real vpos.
-   if ( GFX->getPixelShaderVersion() >= 3.0f )
-      return NULL;
-      */
-
-   // For SM 2.x we need to generate the vpos in the vertex shader
-   // and pass it as a texture coord to the pixel shader.
-
    Var *outVpos = (Var*)LangElement::find( "outVpos" );
    if ( !outVpos )
    {
-      ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>( componentList[C_CONNECTOR] );
-
-      outVpos = connectComp->getElement( RT_TEXCOORD );
-      outVpos->setName( "outVpos" );
-      outVpos->setStructName( "OUT" );
-      outVpos->setType( "vec4" );
+      ShaderConnector* connectComp = dynamic_cast<ShaderConnector*>(componentList[C_CONNECTOR]);
+      outVpos = connectComp->getElement(RT_TEXCOORD);
+      outVpos->setName("outVpos");
+      outVpos->setStructName("OUT");
+      outVpos->setType("vec4");
 
       Var *outPosition = (Var*) LangElement::find( "gl_Position" );
-      AssertFatal( outPosition, "ShaderFeatureGLSL::addOutVpos - Didn't find the output position." );
-
-      meta->addStatement( new GenOp( "   @ = @;\r\n", outVpos, outPosition ) );
+      AssertFatal( outPosition, "ShaderFeatureGLSL::addOutVpos - gl_Position somehow undefined?." );
+      meta->addStatement(new GenOp("   @ = @;\r\n", outVpos, outPosition));
    }
 
    return outVpos;
@@ -469,15 +458,34 @@ Var* ShaderFeatureGLSL::addOutVpos( MultiLine *meta,
 Var* ShaderFeatureGLSL::getInVpos(  MultiLine *meta,
                                     Vector<ShaderComponent*> &componentList )
 {
-   Var *inVpos = (Var*)LangElement::find( "vpos" );
-   if ( inVpos )
+   Var* inVpos = (Var*)LangElement::find("inVpos");
+   if (inVpos)
       return inVpos;
 
-   ShaderConnector *connectComp = dynamic_cast<ShaderConnector*>( componentList[C_CONNECTOR] );
-   inVpos = connectComp->getElement( RT_TEXCOORD );
-   inVpos->setName( "inVpos" );
-   inVpos->setStructName( "IN" );
-   inVpos->setType( "vec4" );
+   ShaderConnector* connectComp = dynamic_cast<ShaderConnector*>(componentList[C_CONNECTOR]);
+   inVpos = connectComp->getElement(RT_TEXCOORD);
+   inVpos->setName("inVpos");
+   inVpos->setStructName("IN");
+   inVpos->setType("vec4");
+
+   Var* targetSize = (Var*)LangElement::find("targetSize");
+   if (!targetSize)
+   {
+      targetSize = new Var();
+      targetSize->setType("vec2");
+      targetSize->setName("targetSize");
+      targetSize->uniform = true;
+      targetSize->constSortPos = cspPotentialPrimitive;
+   }
+
+   // transform projection space to screen space, needs to be done per-pixel. D3D automatically does this with SV_POSITION semantic types (note GLSL doesn't have semantics, even though Torque still uses the RT_ enums for them for some shaderconnector business)
+   // optional: OGL provides this data as gl_FragCoord automatically
+   // Note: for 100% parity with gl_FragCoord (but NOT with vpos in D3D) set .w = 1/.w
+   meta->addStatement(new GenOp("   @.xyz = @.xyz / @.w;\r\n", inVpos, inVpos, inVpos));
+   meta->addStatement(new GenOp("   @.w = @.w;\r\n", inVpos, inVpos)); // for parity w/ gl_FragCoord set: meta->addStatement(new GenOp("    @.w = 1.0 / @.w;\r\n", inVpos, inVpos));
+   meta->addStatement(new GenOp("   @.xy = @.xy * 0.5 + vec2(0.5,0.5);\r\n", inVpos, inVpos)); // get the screen coord to 0 to 1
+   meta->addStatement(new GenOp("   @.y = 1.0 - @.y;\r\n", inVpos, inVpos)); // flip the y axis 
+   meta->addStatement(new GenOp("   @.xy *= @;\r\n", inVpos, targetSize)); // scale to monitor
    return inVpos;
 }
 
@@ -2429,15 +2437,15 @@ void VisibilityFeatGLSL::processPix(   Vector<ShaderComponent*> &componentList,
    else
    {
       visibility = (Var*)LangElement::find( "visibility" );
-      
-	if ( !visibility )
-	{
-		visibility = new Var();
-		visibility->setType( "float" );
-		visibility->setName( "visibility" );
-		visibility->uniform = true;
-		visibility->constSortPos = cspPotentialPrimitive;  
-	}
+
+      if (!visibility)
+      {
+         visibility = new Var();
+         visibility->setType("float");
+         visibility->setName("visibility");
+         visibility->uniform = true;
+         visibility->constSortPos = cspPotentialPrimitive;
+      }
    }
 
 	MultiLine* meta = new MultiLine;      

+ 14 - 12
Engine/source/ts/assimp/assimpAppNode.cpp

@@ -122,15 +122,17 @@ MatrixF AssimpAppNode::getTransform(F32 time)
 void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animSeq)
 {
    // Find the channel for this node
-   for (U32 i = 0; i < animSeq->mNumChannels; ++i)
+   for (U32 k = 0; k < animSeq->mNumChannels; ++k)
    {
-      if (strcmp(mName, animSeq->mChannels[i]->mNodeName.C_Str()) == 0)
+      if (dStrcmp(mName, animSeq->mChannels[k]->mNodeName.C_Str()) == 0)
       {
-         aiNodeAnim *nodeAnim = animSeq->mChannels[i];
+         aiNodeAnim *nodeAnim = animSeq->mChannels[k];
          Point3F trans(Point3F::Zero);
          Point3F scale(Point3F::One);
          QuatF rot;
          rot.identity();
+         // T is in seconds, convert to frames.
+         F32 frame = (t * animSeq->mTicksPerSecond + 0.5f) + 1.0f;
 
          // Transform
          if (nodeAnim->mNumPositionKeys == 1)
@@ -143,13 +145,13 @@ void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animS
             {
                F32 curT = sTimeMultiplier * (F32)nodeAnim->mPositionKeys[key].mTime;
                curPos.set(nodeAnim->mPositionKeys[key].mValue.x, nodeAnim->mPositionKeys[key].mValue.y, nodeAnim->mPositionKeys[key].mValue.z);
-               if ((curT > t) && (key > 0))
+               if ((curT > frame) && (key > 0))
                {
-                  F32 factor = (t - lastT) / (curT - lastT);
+                  F32 factor = (frame - lastT) / (curT - lastT);
                   trans.interpolate(lastPos, curPos, factor);
                   break;
                }
-               else if ((curT >= t) || (key == nodeAnim->mNumPositionKeys - 1))
+               else if ((curT >= frame) || (key == nodeAnim->mNumPositionKeys - 1))
                {
                   trans = curPos;
                   break;
@@ -173,13 +175,13 @@ void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animS
                F32 curT = sTimeMultiplier * (F32)nodeAnim->mRotationKeys[key].mTime;
                curRot.set(nodeAnim->mRotationKeys[key].mValue.x, nodeAnim->mRotationKeys[key].mValue.y,
                   nodeAnim->mRotationKeys[key].mValue.z, nodeAnim->mRotationKeys[key].mValue.w);
-               if ((curT > t) && (key > 0))
+               if ((curT > frame) && (key > 0))
                {
-                  F32 factor = (t - lastT) / (curT - lastT);
+                  F32 factor = (frame - lastT) / (curT - lastT);
                   rot.interpolate(lastRot, curRot, factor);
                   break;
                }
-               else if ((curT >= t) || (key == nodeAnim->mNumRotationKeys - 1))
+               else if ((curT >= frame) || (key == nodeAnim->mNumRotationKeys - 1))
                {
                   rot = curRot;
                   break;
@@ -201,13 +203,13 @@ void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animS
             {
                F32 curT = sTimeMultiplier * (F32)nodeAnim->mScalingKeys[key].mTime;
                curScale.set(nodeAnim->mScalingKeys[key].mValue.x, nodeAnim->mScalingKeys[key].mValue.y, nodeAnim->mScalingKeys[key].mValue.z);
-               if ((curT > t) && (key > 0))
+               if ((curT > frame) && (key > 0))
                {
-                  F32 factor = (t - lastT) / (curT - lastT);
+                  F32 factor = (frame - lastT) / (curT - lastT);
                   scale.interpolate(lastScale, curScale, factor);
                   break;
                }
-               else if ((curT >= t) || (key == nodeAnim->mNumScalingKeys - 1))
+               else if ((curT >= frame) || (key == nodeAnim->mNumScalingKeys - 1))
                {
                   scale = curScale;
                   break;

+ 47 - 44
Engine/source/ts/assimp/assimpAppSequence.cpp

@@ -14,72 +14,75 @@
 
 AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
    seqStart(0.0f),
-   mAnim(a)
+   seqEnd(0.0f)
 {
+   mAnim = new aiAnimation(*a);
+   // Deep copy channels
+   mAnim->mChannels = new aiNodeAnim * [a->mNumChannels];
+   for (U32 i = 0; i < a->mNumChannels; ++i) {
+      mAnim->mChannels[i] = new aiNodeAnim(*a->mChannels[i]);
+   }
+
+   // Deep copy meshes
+   mAnim->mMeshChannels = new aiMeshAnim * [a->mNumMeshChannels];
+   for (U32 i = 0; i < a->mNumMeshChannels; ++i) {
+      mAnim->mMeshChannels[i] = new aiMeshAnim(*a->mMeshChannels[i]);
+   }
+
+   // Deep copy name
+   mAnim->mName = a->mName;
+
    mSequenceName = mAnim->mName.C_Str();
    if (mSequenceName.isEmpty())
       mSequenceName = "ambient";
    Con::printf("\n[Assimp] Adding %s animation", mSequenceName.c_str());
 
-   fps = (mAnim->mTicksPerSecond > 0) ? mAnim->mTicksPerSecond : 30.0f;
+   fps = (a->mTicksPerSecond > 0) ? a->mTicksPerSecond : 30.0f;
 
-   U32 maxKeys = 0;
-   F32 maxEndTime = 0;
-   F32 minFrameTime = 100000.0f;
-   // Detect the frame rate (minimum time between keyframes) and max sequence time
-   for (U32 i = 0; i < mAnim->mNumChannels; ++i)
+   if (a->mDuration > 0)
    {
-      aiNodeAnim *nodeAnim = mAnim->mChannels[i];
-      maxKeys = getMax(maxKeys, nodeAnim->mNumPositionKeys);
-      maxKeys = getMax(maxKeys, nodeAnim->mNumRotationKeys);
-      maxKeys = getMax(maxKeys, nodeAnim->mNumScalingKeys);
-
-      if (nodeAnim->mNumPositionKeys)
-         maxEndTime = getMax(maxEndTime, (F32) nodeAnim->mPositionKeys[nodeAnim->mNumPositionKeys-1].mTime);
-      if (nodeAnim->mNumRotationKeys)
-         maxEndTime = getMax(maxEndTime, (F32) nodeAnim->mRotationKeys[nodeAnim->mNumRotationKeys-1].mTime);
-      if (nodeAnim->mNumScalingKeys)
-         maxEndTime = getMax(maxEndTime, (F32) nodeAnim->mScalingKeys[nodeAnim->mNumScalingKeys-1].mTime);
-
-      for (U32 key = 1; key < nodeAnim->mNumPositionKeys; ++key)
-      {
-         F32 deltaT = nodeAnim->mPositionKeys[key].mTime - nodeAnim->mPositionKeys[key-1].mTime;
-         minFrameTime = getMin(minFrameTime, deltaT);
-      }
-      for (U32 key = 1; key < nodeAnim->mNumRotationKeys; ++key)
-      {
-         F32 deltaT = nodeAnim->mRotationKeys[key].mTime - nodeAnim->mRotationKeys[key-1].mTime;
-         minFrameTime = getMin(minFrameTime, deltaT);
-      }
-      for (U32 key = 1; key < nodeAnim->mNumScalingKeys; ++key)
+      // torques seqEnd is in seconds, this then gets generated into frames in generateSequences()
+      seqEnd = (F32)a->mDuration / fps;
+   }
+   else
+   {
+      for (U32 i = 0; i < a->mNumChannels; ++i)
       {
-         F32 deltaT = nodeAnim->mScalingKeys[key].mTime - nodeAnim->mScalingKeys[key-1].mTime;
-         minFrameTime = getMin(minFrameTime, deltaT);
+         aiNodeAnim* nodeAnim = a->mChannels[i];
+         // Determine the maximum keyframe time for this animation
+         F32 maxKeyTime = 0.0f;
+         for (U32 k = 0; k < nodeAnim->mNumPositionKeys; k++) {
+            maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mPositionKeys[k].mTime);
+         }
+         for (U32 k = 0; k < nodeAnim->mNumRotationKeys; k++) {
+            maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mRotationKeys[k].mTime);
+         }
+         for (U32 k = 0; k < nodeAnim->mNumScalingKeys; k++) {
+            maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mScalingKeys[k].mTime);
+         }
+
+         seqEnd = getMax(seqEnd, maxKeyTime);
       }
    }
 
+   mTimeMultiplier = 1.0f;
+
    S32 timeFactor = ColladaUtils::getOptions().animTiming;
-   S32 fpsRequest = ColladaUtils::getOptions().animFPS;
+   S32 fpsRequest = (S32)a->mTicksPerSecond;
    if (timeFactor == 0)
    {  // Timing specified in frames
       fps = mClamp(fpsRequest, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate);
-      maxKeys = getMax(maxKeys, (U32)maxEndTime);  // Keys won't be assigned for every frame.
-      seqEnd = maxKeys / fps;
       mTimeMultiplier = 1.0f / fps;
    }
    else
    {  // Timing specified in seconds or ms depending on format
-      if (maxEndTime > 1000.0f || mAnim->mDuration > 1000.0f)
+      if (seqEnd > 1000.0f || a->mDuration > 1000.0f)
          timeFactor = 1000.0f;   // If it's more than 1000 seconds, assume it's ms.
 
       timeFactor = mClamp(timeFactor, 1, 1000);
-      minFrameTime /= (F32)timeFactor;
-      maxEndTime /= (F32)timeFactor;
-      fps = (minFrameTime > 0.0f) ? 1.0f / minFrameTime : fps;
-      fps = mClamp(fpsRequest, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate);
-      seqEnd = maxEndTime;
       mTimeMultiplier = 1.0f / timeFactor;
    }
+
 }
 
 AssimpAppSequence::~AssimpAppSequence()
@@ -102,7 +105,7 @@ void AssimpAppSequence::setActive(bool active)
 
 U32 AssimpAppSequence::getFlags() const 
 { 
-   return TSShape::Blend;
+   return TSShape::Cyclic;
 }
 F32 AssimpAppSequence::getPriority() const 
 { 
@@ -110,5 +113,5 @@ F32 AssimpAppSequence::getPriority() const
 }
 F32 AssimpAppSequence::getBlendRefTime() const 
 { 
-   return -1.0f; 
-}
+   return 0.0f; 
+}

+ 1 - 1
Engine/source/ts/assimp/assimpAppSequence.h

@@ -48,4 +48,4 @@ public:
    virtual U32 getFlags() const;
    virtual F32 getPriority() const;
    virtual F32 getBlendRefTime() const;
-};
+};

+ 58 - 9
Engine/source/ts/assimp/assimpShapeLoader.cpp

@@ -229,6 +229,18 @@ void AssimpShapeLoader::enumerateScene()
       if (!processNode(node))
          delete node;
 
+      // add bounds node.
+      if (!boundsNode)
+      {
+         aiNode* req[1];
+         req[0] = new aiNode("bounds");
+         mScene->mRootNode->addChildren(1, req);
+
+         AssimpAppNode* appBounds = new AssimpAppNode(mScene, req[0]);
+         if (!processNode(appBounds))
+            delete appBounds;
+      }
+
       // Check for animations and process those.
       processAnimations();
    } 
@@ -243,12 +255,45 @@ void AssimpShapeLoader::enumerateScene()
 
 void AssimpShapeLoader::processAnimations()
 {
-   for(U32 n = 0; n < mScene->mNumAnimations; ++n)
+   // add all animations into 1 ambient animation.
+   aiAnimation* ambientSeq = new aiAnimation();
+   ambientSeq->mName = "ambient";
+
+   Vector<aiNodeAnim*> ambientChannels;
+   F32 duration = 0.0f;
+   if (mScene->mNumAnimations > 0)
    {
-      Con::printf("[ASSIMP] Animation Found: %s", mScene->mAnimations[n]->mName.C_Str());
+      for (U32 i = 0; i < mScene->mNumAnimations; ++i)
+      {
+         aiAnimation* anim = mScene->mAnimations[i];
+         for (U32 j = 0; j < anim->mNumChannels; j++)
+         {
+            aiNodeAnim* nodeAnim = anim->mChannels[j];
+            // Determine the maximum keyframe time for this animation
+            F32 maxKeyTime = 0.0f;
+            for (U32 k = 0; k < nodeAnim->mNumPositionKeys; k++) {
+               maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mPositionKeys[k].mTime);
+            }
+            for (U32 k = 0; k < nodeAnim->mNumRotationKeys; k++) {
+               maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mRotationKeys[k].mTime);
+            }
+            for (U32 k = 0; k < nodeAnim->mNumScalingKeys; k++) {
+               maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mScalingKeys[k].mTime);
+            }
 
-      AssimpAppSequence* newAssimpSeq = new AssimpAppSequence(mScene->mAnimations[n]);
-      appSequences.push_back(newAssimpSeq);
+            ambientChannels.push_back(nodeAnim);
+
+            duration = getMax(duration, maxKeyTime);
+         }
+      }
+
+      ambientSeq->mNumChannels = ambientChannels.size();
+      ambientSeq->mChannels = ambientChannels.address();
+      ambientSeq->mDuration = duration;
+      ambientSeq->mTicksPerSecond = 24.0;
+
+      AssimpAppSequence* defaultAssimpSeq = new AssimpAppSequence(ambientSeq);
+      appSequences.push_back(defaultAssimpSeq);
    }
 }
 
@@ -369,12 +414,16 @@ bool AssimpShapeLoader::fillGuiTreeView(const char* sourceShapePath, GuiTreeView
       tree->insertItem(matItem, String::ToString("%s", name.c_str()), String::ToString("%s", texName.c_str()));
    }
 
-   for (U32 i = 0; i < shapeScene->mNumAnimations; i++)
+   if (shapeScene->mNumAnimations == 0)
    {
-      String sequenceName = shapeScene->mAnimations[i]->mName.C_Str();
-      if (sequenceName.isEmpty())
-         sequenceName = "ambient";
-      tree->insertItem(animItem, sequenceName.c_str());
+      tree->insertItem(animItem, "ambient", "animation", "", 0, 0);
+   }
+   else
+   {
+      for (U32 i = 0; i < shapeScene->mNumAnimations; i++)
+      {
+         tree->insertItem(animItem, shapeScene->mAnimations[i]->mName.C_Str(), "animation", "", 0, 0);
+      }
    }
 
    U32 numNodes = 0;

+ 40 - 0
LICENSE.md

@@ -19,3 +19,43 @@ 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.
+
+
+IES Profile Generator tool: https://github.com/nickmcdonald/ies-generator
+
+// +----------------------------------------------------------------------
+// | Project : ray.
+// | All rights reserved.
+// +----------------------------------------------------------------------
+// | Copyright (c) 2013-2017.
+// +----------------------------------------------------------------------
+// | * Redistribution and use of this software in source and binary forms,
+// |   with or without modification, are permitted provided that the following
+// |   conditions are met:
+// |
+// | * Redistributions of source code must retain the above
+// |   copyright notice, this list of conditions and the
+// |   following disclaimer.
+// |
+// | * Redistributions in binary form must reproduce the above
+// |   copyright notice, this list of conditions and the
+// |   following disclaimer in the documentation and/or other
+// |   materials provided with the distribution.
+// |
+// | * Neither the name of the ray team, nor the names of its
+// |   contributors may be used to endorse or promote products
+// |   derived from this software without specific prior
+// |   written permission of the ray team.
+// |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// +----------------------------------------------------------------------

+ 5 - 2
Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript

@@ -118,6 +118,7 @@ singleton GFXStateBlockData( AL_ConvexLightState )
    samplerStates[3] = SamplerClampPoint;  // colorBuffer
    samplerStates[4] = SamplerClampPoint;  // matInfoBuffer
    samplerStates[5] = SamplerClampLinear; // Cookie Map
+   samplerStates[6] = SamplerClampLinear; // iesProfile
    
    cullDefined = true;
    cullMode = GFXCullCW;
@@ -142,6 +143,7 @@ singleton shaderData( AL_PointLightShader )
    samplerNames[3] = "$colorBuffer";
    samplerNames[4] = "$matInfoBuffer";
    samplerNames[5] = "$cookieMap";
+   samplerNames[6] = "$iesProfile";
    
    pixVersion = 3.0;
 };
@@ -156,7 +158,7 @@ singleton CustomMaterial( AL_PointLightMaterial )
    sampler["cookieMap"] = "$dynamiclightmask";
    sampler["colorBuffer"] = "#color";
    sampler["matInfoBuffer"] = "#matinfo";
-   
+   sampler["iesProfile"] = "$photometricmask";
    target = "AL_FormatToken";
    
    pixVersion = 3.0;
@@ -177,6 +179,7 @@ singleton shaderData( AL_SpotLightShader )
    samplerNames[3] = "$colorBuffer";
    samplerNames[4] = "$matInfoBuffer";
    samplerNames[5] = "$cookieMap";
+   samplerNames[6] = "$iesProfile";
 
    pixVersion = 3.0;
 };
@@ -188,10 +191,10 @@ singleton CustomMaterial( AL_SpotLightMaterial )
    
    sampler["deferredBuffer"] = "#deferred";
    sampler["shadowMap"] = "$dynamiclight";
+   sampler["iesProfile"] = "$photometricmask";
    sampler["cookieMap"] = "$dynamiclightmask";
    sampler["colorBuffer"] = "#color";
    sampler["matInfoBuffer"] = "#matinfo";
-   
    target = "AL_FormatToken";
    
    pixVersion = 3.0;

+ 24 - 1
Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/pointLightP.glsl

@@ -108,14 +108,22 @@ uniform sampler2D deferredBuffer;
 #include "softShadow.glsl"
 uniform sampler2D colorBuffer;
 uniform sampler2D matInfoBuffer;
-#ifdef USE_COOKIE_TEX
+
+#ifdef SHADOW_CUBE
 /// The texture for cookie rendering.
 uniform samplerCube cookieMap;
+#else
+uniform sampler2D cookieMap;
+#endif
+
+#ifdef UES_PHOTOMETRIC_MASK
+uniform sampler1D iesProfile;
 #endif
 
 uniform vec4 rtParams0;
 
 uniform vec3 lightPosition;
+uniform vec3 lightDirection;
 uniform vec4 lightColor;
 uniform float  lightBrightness;
 uniform float  lightRange;
@@ -181,7 +189,12 @@ void main()
    #ifdef USE_COOKIE_TEX
 
       // Lookup the cookie sample.
+      #ifdef SHADOW_CUBE
       vec4 cookie = texture(cookieMap, tMul(worldToLightProj, -surfaceToLight.L));
+      #else
+      vec2 cookieCoord = decodeShadowCoord( tMul( worldToLightProj, -surfaceToLight.L ) ).xy;
+      vec4 cookie = texture(cookieMap, cookieCoord);
+      #endif
       // Multiply the light with the cookie tex.
       lightCol *= cookie.rgb;
       // Use a maximum channel luminance to attenuate 
@@ -221,9 +234,19 @@ void main()
       OUT_col = vec4(final, 0);
       return
    #endif
+   
+   #ifdef UES_PHOTOMETRIC_MASK
+      // Lookup the cookie sample.d
+      float cosTheta = dot(-surfaceToLight.L, lightDirection); 
+      float angle = acos(cosTheta) * ( M_1OVER_PI_F); 
+      float iesMask = texture(iesProfile,angle).r; 
+      // Multiply the light with the iesMask tex.
+      shadow *= iesMask;
+   #endif
 
       //get punctual light contribution   
       lighting = getPunctualLight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, shadow);
+   
    }
 
    OUT_col = vec4(lighting, 0);

+ 33 - 15
Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/spotLightP.glsl

@@ -19,7 +19,6 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
-
 #include "../../../gl/hlslCompat.glsl"
 #include "farFrustumQuad.glsl"
 #include "../../shadowMap/shadowMapIO_GLSL.h"
@@ -38,11 +37,15 @@ uniform sampler2D shadowMap;
 #include "softShadow.glsl"
 uniform sampler2D colorBuffer;
 uniform sampler2D matInfoBuffer;
+
 #ifdef USE_COOKIE_TEX
-/// The texture for cookie rendering.
 uniform sampler2D cookieMap;
 #endif
 
+#ifdef UES_PHOTOMETRIC_MASK
+uniform sampler1D iesProfile;
+#endif
+
 uniform vec4 rtParams0;
 
 uniform float  lightBrightness;
@@ -91,7 +94,7 @@ void main()
 	if(dist < lightRange)
    {
       SurfaceToLight surfaceToLight = createSurfaceToLight(surface, L);
-      vec3 lightCol = lightColor.rgb;
+      
       
       float shadow = 1.0;
       #ifndef NO_SHADOW
@@ -105,19 +108,25 @@ void main()
          //distance to light in shadow map space
          float distToLight = pxlPosLightProj.z / lightRange;
          shadow = softShadow_filter(shadowMap, ssPos.xy/ssPos.w, shadowCoord, shadowSoftness, distToLight, surfaceToLight.NdotL, lightParams.y);
-         #ifdef USE_COOKIE_TEX
-            // Lookup the cookie sample.
-            vec4 cookie = texture(cookieMap, shadowCoord);
-            // Multiply the light with the cookie tex.
-            lightCol *= cookie.rgb;
-            // Use a maximum channel luminance to attenuate 
-            // the lighting else we get specular in the dark
-            // regions of the cookie texture.
-            lightCol *= max(cookie.r, max(cookie.g, cookie.b));
-         #endif
+         
       }
-      #endif      
-   
+      #endif
+
+   vec3 lightCol = lightColor.rgb;
+   #ifdef USE_COOKIE_TEX
+      // Lookup the cookie sample.
+      vec4 pxlPosLightProj = tMul( worldToLightProj, vec4( surface.P, 1 ) );
+      vec2 cookieCoord = ( ( pxlPosLightProj.xy / pxlPosLightProj.w ) * 0.5 ) + vec2( 0.5, 0.5 );
+      cookieCoord.y = 1.0f - cookieCoord.y;
+      vec4 cookie = texture(cookieMap, cookieCoord);
+      // Multiply the light with the cookie tex.
+      lightCol *= cookie.rgb;
+      // Use a maximum channel luminance to attenuate 
+      // the lighting else we get specular in the dark
+      // regions of the cookie texture.
+      lightCol *= max(cookie.r, max(cookie.g, cookie.b));
+   #endif
+
 
    #ifdef DIFFUSE_LIGHT_VIZ
       float attenuation = getDistanceAtt(surfaceToLight.Lu, radius);
@@ -154,6 +163,15 @@ void main()
       return;
    #endif
 
+   #ifdef UES_PHOTOMETRIC_MASK
+      // Lookup the cookie sample.d
+      float cosTheta = dot(-surfaceToLight.L, lightDirection); 
+      float angle = acos(cosTheta) * ( M_1OVER_PI_F); 
+      float iesMask = texture(iesProfile, angle/(lightSpotParams.x-lightSpotParams.y)).r; 
+      // Multiply the light with the iesMask tex.
+      shadow *= iesMask;
+   #endif
+      
       //get spot light contribution   
       lighting = getSpotlight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, lightDirection, lightSpotParams, shadow);
    }

+ 37 - 16
Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/pointLightP.hlsl

@@ -107,9 +107,16 @@ TORQUE_UNIFORM_SAMPLER2D(shadowMap, 1);
 #include "softShadow.hlsl"
 TORQUE_UNIFORM_SAMPLER2D(colorBuffer, 3);
 TORQUE_UNIFORM_SAMPLER2D(matInfoBuffer, 4);
-#ifdef USE_COOKIE_TEX
+
 /// The texture for cookie rendering.
+#ifdef SHADOW_CUBE
 TORQUE_UNIFORM_SAMPLERCUBE(cookieMap, 5);
+#else
+TORQUE_UNIFORM_SAMPLER2D(cookieMap, 5);
+#endif
+
+#ifdef UES_PHOTOMETRIC_MASK
+TORQUE_UNIFORM_SAMPLER1D(iesProfile, 6);
 #endif
 
 uniform float4 rtParams0;
@@ -117,6 +124,7 @@ uniform float4 lightColor;
 
 uniform float  lightBrightness;
 uniform float3 lightPosition;
+uniform float3 lightDirection;
 
 uniform float4 lightMapParams;
 uniform float4 vsFarPlane;
@@ -158,28 +166,31 @@ float4 main(   ConvexConnectP IN ) : SV_TARGET
 	{     
       float distToLight = dist / lightRange;
       SurfaceToLight surfaceToLight = createSurfaceToLight(surface, L);
-
-   float shadow = 1.0;
+      float3 lightCol = lightColor.rgb;
+      float shadow = 1.0;
    #ifndef NO_SHADOW
    if (getFlag(surface.matFlag, 0)) //also skip if we don't recieve shadows
    {
-   #ifdef SHADOW_CUBE
+      #ifdef SHADOW_CUBE
 
-      // TODO: We need to fix shadow cube to handle soft shadows!
-      float occ = TORQUE_TEXCUBE( shadowMap, mul( worldToLightProj, -surfaceToLight.L ) ).r;
-      shadow = saturate( exp( lightParams.y * ( occ - distToLight ) ) );
+         // TODO: We need to fix shadow cube to handle soft shadows!
+         float occ = TORQUE_TEXCUBE( shadowMap, mul( worldToLightProj, -surfaceToLight.L ) ).r;
+         shadow = saturate( exp( lightParams.y * ( occ - distToLight ) ) );
 
-   #else
-      float2 shadowCoord = decodeShadowCoord( mul( worldToLightProj, -surfaceToLight.L ) ).xy;
-      shadow = softShadow_filter(TORQUE_SAMPLER2D_MAKEARG(shadowMap), ssPos.xy, shadowCoord, shadowSoftness, distToLight, surfaceToLight.NdotL, lightParams.y);
-   #endif
+      #else
+         float2 shadowCoord = decodeShadowCoord( mul( worldToLightProj, -surfaceToLight.L ) ).xy;
+         shadow = softShadow_filter(TORQUE_SAMPLER2D_MAKEARG(shadowMap), ssPos.xy, shadowCoord, shadowSoftness, distToLight, surfaceToLight.NdotL, lightParams.y);
+      #endif
    }
    #endif // !NO_SHADOW
-   
-      float3 lightCol = lightColor.rgb;
    #ifdef USE_COOKIE_TEX
       // Lookup the cookie sample.
+      #ifdef SHADOW_CUBE
       float4 cookie = TORQUE_TEXCUBE(cookieMap, mul(worldToLightProj, -surfaceToLight.L));
+      #else
+      float2 cookieCoord = decodeShadowCoord( mul( worldToLightProj, -surfaceToLight.L ) ).xy;
+      float4 cookie = TORQUE_TEX2D(cookieMap, cookieCoord);
+      #endif
       // Multiply the light with the cookie tex.
       lightCol *= cookie.rgb;
       // Use a maximum channel luminance to attenuate 
@@ -187,7 +198,6 @@ float4 main(   ConvexConnectP IN ) : SV_TARGET
       // regions of the cookie texture.
       lightCol *= max(cookie.r, max(cookie.g, cookie.b));
    #endif
-
    #ifdef DIFFUSE_LIGHT_VIZ
       float attenuation = getDistanceAtt(surfaceToLight.Lu, radius);
       float3 factor = lightColor * max(surfaceToLight.NdotL, 0) * shadow * lightIntensity * attenuation;
@@ -198,7 +208,7 @@ float4 main(   ConvexConnectP IN ) : SV_TARGET
    #endif
 
    #ifdef SPECULAR_LIGHT_VIZ
-   float attenuation = getDistanceAtt(surfaceToLight.Lu, radius);
+      float attenuation = getDistanceAtt(surfaceToLight.Lu, radius);
       float3 factor = lightColor * max(surfaceToLight.NdotL, 0) * shadow * lightIntensity * attenuation;
 
       float3 diffuse = BRDF_GetDebugSpecular(surface,surfaceToLight) * factor;
@@ -217,9 +227,20 @@ float4 main(   ConvexConnectP IN ) : SV_TARGET
       return final;
    #endif
 
+   #ifdef UES_PHOTOMETRIC_MASK
+      // Lookup the cookie sample.d
+      float cosTheta = dot(-surfaceToLight.L, lightDirection); 
+      float angle = acos(cosTheta) * ( M_1OVER_PI_F); 
+      float iesMask = TORQUE_TEX1D(iesProfile, angle).r;
+      // Multiply the light with the iesMask tex.
+      shadow *= iesMask;
+   #endif
+   
       //get punctual light contribution   
       lighting = getPunctualLight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, shadow);
+
    }
-      
+   
+
    return float4(lighting, 0);
 }

+ 30 - 13
Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/spotLightP.hlsl

@@ -42,11 +42,16 @@ TORQUE_UNIFORM_SAMPLER2D(shadowMap, 1);
 #include "softShadow.hlsl"
 TORQUE_UNIFORM_SAMPLER2D(colorBuffer, 3);
 TORQUE_UNIFORM_SAMPLER2D(matInfoBuffer, 4);
-#ifdef USE_COOKIE_TEX
 /// The texture for cookie rendering.
+
+#ifdef USE_COOKIE_TEX
 TORQUE_UNIFORM_SAMPLER2D(cookieMap, 5);
+#endif
 
+#ifdef UES_PHOTOMETRIC_MASK
+TORQUE_UNIFORM_SAMPLER1D(iesProfile, 6);
 #endif
+
 uniform float4 rtParams0;
 
 uniform float  lightBrightness;
@@ -96,8 +101,7 @@ float4 main(   ConvexConnectP IN ) : SV_TARGET
 	if(dist < lightRange)
 	{     
       SurfaceToLight surfaceToLight = createSurfaceToLight(surface, L);
-      float3 lightCol = lightColor.rgb;
-         
+      
       float shadow = 1.0; 
       #ifndef NO_SHADOW      
       if (getFlag(surface.matFlag, 0)) //also skip if we don't recieve shadows
@@ -109,19 +113,23 @@ float4 main(   ConvexConnectP IN ) : SV_TARGET
          //distance to light in shadow map space
          float distToLight = pxlPosLightProj.z / lightRange;
          shadow = softShadow_filter(TORQUE_SAMPLER2D_MAKEARG(shadowMap), ssPos.xy, shadowCoord, shadowSoftness, distToLight, surfaceToLight.NdotL, lightParams.y);
-         #ifdef USE_COOKIE_TEX
-            // Lookup the cookie sample.
-            float4 cookie = TORQUE_TEX2D(cookieMap, shadowCoord);
-            // Multiply the light with the cookie tex.
-            lightCol *= cookie.rgb;
-            // Use a maximum channel luminance to attenuate 
-            // the lighting else we get specular in the dark
-            // regions of the cookie texture.
-            lightCol *= max(cookie.r, max(cookie.g, cookie.b));
-         #endif
       }
       #endif
 
+   float3 lightCol = lightColor.rgb;
+   #ifdef USE_COOKIE_TEX
+      float4 pxlPosLightProj = mul( worldToLightProj, float4( surface.P, 1 ) );
+      float2 cookieCoord = ( ( pxlPosLightProj.xy / pxlPosLightProj.w ) * 0.5 ) + float2( 0.5, 0.5 );
+      // Lookup the cookie sample.
+      float4 cookie = TORQUE_TEX2D(cookieMap, cookieCoord);
+      // Multiply the light with the cookie tex.
+      lightCol *= cookie.rgb;
+      // Use a maximum channel luminance to attenuate 
+      // the lighting else we get specular in the dark
+      // regions of the cookie texture.
+      lightCol *= max(cookie.r, max(cookie.g, cookie.b));
+   #endif
+
    #ifdef DIFFUSE_LIGHT_VIZ
       float attenuation = getDistanceAtt(surfaceToLight.Lu, radius);
       float3 factor = lightColor * max(surfaceToLight.NdotL, 0) * shadow * lightIntensity * attenuation;
@@ -151,6 +159,15 @@ float4 main(   ConvexConnectP IN ) : SV_TARGET
       return final;
    #endif
 
+      #ifdef UES_PHOTOMETRIC_MASK
+         // Lookup the cookie sample.d
+         float cosTheta = dot(-surfaceToLight.L, lightDirection); 
+         float angle = acos(cosTheta) * ( M_1OVER_PI_F); 
+         float iesMask = TORQUE_TEX1D(iesProfile, angle/(lightSpotParams.x-lightSpotParams.y)).r;
+         // Multiply the light with the iesMask tex.
+         shadow *= iesMask;
+      #endif 
+      
       //get spot light contribution   
       lighting = getSpotlight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, lightDirection, lightSpotParams, shadow);
    }

+ 1 - 1
Templates/BaseGame/game/core/utility/scripts/helperFunctions.tscript

@@ -650,7 +650,7 @@ function populateAllFonts()
          continue;
       %fontarray.push_back(%obj.fontType,%obj.fontSize);
    }
-   %fontarray.uniqueKey();
+   %fontarray.uniquePair();
    
    %fontarrayCount = %fontarray.count(); 
    

+ 80 - 0
Templates/BaseGame/game/data/Prototyping/iesprofiles/jelly-fish.ies

@@ -0,0 +1,80 @@
+IESNA:LM-63-1995
+[TEST]BALLABS TEST NO.  12788.0 
+[MANUFAC] LeoMoon Studios
+[LUMINAIRE] 1/100W ICETRON INDUCTION 20x15"DIRECT AREALIGHT LUMINAIRE
+[LUMINAIRE] MIRO4 SPEC ALUM TYPE II OPTICS & CLEAR FLAT GLASS LENS   
+[LUMINAIRE] BLACK PANEL STREET SIDE  SYLVANIA # QT 1x150 ICE/UNV-T   
+[LUMCAT] ICEAL2-150S-TYPE2/CG1-277                   
+[LAMPCAT] ICE100/QT150 2PIN  
+TILT=NONE
+   1  11000.  1.000000   25   21    1    1 .938  1.266  .000
+    1.0000    1.0000  140.0000
+     .0    5.0   10.0   15.0   20.0   25.0   30.0   35.0   40.0   45.0
+   50.0   55.0   60.0   62.5   65.0   67.5   70.0   72.5   75.0   77.5
+   80.0   82.5   85.0   87.5   90.0
+     .0    5.0   15.0   25.0   35.0   45.0   55.0   65.0   75.0   85.0
+   90.0   95.0  105.0  115.0  125.0  135.0  145.0  155.0  165.0  175.0
+  180.0
+  1789.  1841.  1911.  1979.  2023.  2025.  1994.  1867.  1718.  1551.
+  1374.  1208.   990.   808.   637.   469.   347.   202.    89.    18.
+     9.     7.     7.     6.     0.
+  1789.  1831.  1908.  1969.  2013.  2018.  1983.  1865.  1701.  1548.
+  1353.  1193.   981.   842.   661.   503.   352.   172.    68.    19.
+     8.     6.     6.     5.     0.
+  1789.  1828.  1891.  1939.  1958.  1939.  1908.  1808.  1668.  1559.
+  1428.  1300.  1222.  1161.   999.   778.   580.   318.   129.    39.
+    10.     7.     7.     4.     0.
+  1789.  1818.  1865.  1891.  1942.  1959.  1927.  1843.  1761.  1714.
+  1673.  1599.  1445.  1325.  1165.   948.   668.   400.   186.    75.
+    30.     9.     7.     2.     0.
+  1789.  1811.  1821.  1891.  1941.  1974.  1980.  1943.  1993.  2082.
+  1963.  1808.  1621.  1601.  1424.  1166.   829.   487.   279.   152.
+    65.    15.     8.     2.     0.
+  1789.  1794.  1804.  1893.  1935.  1995.  2020.  2145.  2342.  2221.
+  2063.  2135.  2157.  1694.   948.   340.   101.     8.     0.   185.
+    94.    32.     9.     0.     0.
+  1789.  1775.  1803.  1868.  1931.  1992.  2076.  2357.  2333.  2285.
+  2254.  2608.  2375.  2010.  1722.  1448.   981.   661.   376.   244.
+   122.    38.     8.     0.     0.
+  1789.  1763.  1798.  1839.  1918.  1961.  2151.  2291.  2310.  2263.
+  2575.  2700.  2380.  2075.  1792.  1483.  1102.   702.   430.   298.
+   192.    67.    11.     2.     0.
+  1789.  1746.  1801.  1827.  1871.  1923.  2176.  2119.  2124.  2062.
+  2553.  2596.  2201.  1907.  1678.  1368.  1015.   673.   440.   303.
+   178.    63.    10.     1.     0.
+  1789.  1738.  1788.  1808.  1830.  1895.  2081.  2024.  1953.  1883.
+  2313.  2285.  1854.  1661.  1400.  1139.   833.   577.   404.   267.
+   157.    54.    12.     1.     0.
+  1789.  1735.  1783.  1797.  1812.  1868.  2046.  1983.  1911.  1805.
+  2191.  2151.  1759.  1513.  1288.  1033.   802.   568.   386.   243.
+   134.    56.    10.     3.     0.
+  1789.  1734.  1778.  1786.  1797.  1838.  2016.  1951.  1871.  1766.
+  2128.  2078.  1681.  1474.  1248.  1009.   739.   523.   349.   228.
+   144.    52.    13.     2.     0.
+  1789.  1733.  1774.  1773.  1771.  1777.  1959.  1895.  1811.  1647.
+  2034.  1957.  1595.  1293.  1118.   925.   717.   481.   319.   208.
+   125.    51.     7.     1.     0.
+  1789.  1738.  1763.  1755.  1744.  1704.  1880.  1794.  1752.  1541.
+  1772.  1720.  1427.  1191.  1003.   795.   588.   427.   274.   166.
+    98.    39.     8.     3.     0.
+  1789.  1734.  1744.  1732.  1708.  1647.  1674.  1703.  1620.  1413.
+  1325.  1423.  1205.   983.   808.   642.   464.   312.   192.   107.
+    57.    18.     8.     3.     0.
+  1789.  1735.  1714.  1725.  1673.  1590.  1481.  1511.  1373.  1230.
+   982.   993.   902.   771.   626.   456.   300.   187.   112.    60.
+    31.    14.     8.     4.     0.
+  1789.  1736.  1681.  1700.  1617.  1511.  1381.  1252.  1210.  1043.
+   875.   657.   554.   487.   398.   294.   195.   122.    70.    40.
+    19.    11.     8.     3.     0.
+  1789.  1737.  1666.  1641.  1577.  1471.  1333.  1172.  1015.   890.
+   728.   574.   409.   326.   243.   181.   114.    72.    40.    24.
+    14.     9.     7.     4.     0.
+  1789.  1740.  1679.  1601.  1493.  1388.  1268.  1114.   956.   769.
+   624.   481.   347.   267.   201.   141.    85.    41.    25.    16.
+    10.     7.     6.     3.     0.
+  1789.  1737.  1693.  1616.  1479.  1340.  1187.  1041.   896.   724.
+   572.   431.   303.   233.   167.   117.    62.    29.    18.    12.
+     9.     6.     6.     4.     1.
+  1789.  1742.  1698.  1623.  1491.  1354.  1196.  1040.   894.   722.
+   565.   421.   302.   227.   159.   108.    57.    20.    14.    10.
+     8.     6.     6.     5.     0.

+ 16 - 0
Templates/BaseGame/game/data/Prototyping/iesprofiles/top-post.ies

@@ -0,0 +1,16 @@
+IESNA:LM-63-2002
+[TEST]BALLABS TEST NO.  14501.0
+[TESTLAB] BUILDING ACOUSTICS & LIGHTING LABORATORIES, INC
+[ISSUEDATE] 04-MAR-2009
+[MANUFAC] LeoMoon Studios
+[LUMINAIRE] 1/100W CLEAR ED17PS MH HORIZ LAMP LS SERIES POST LUMINAIRE
+[MORE] WHITE TOP REFLECTOR w/FROSTED GLASS CHIMNEY              
+[MORE] CLEAR ACRYLIC PANELS                                     
+[LUMCAT] HLSC15-100PSMH120-BLK                       
+[LAMPCAT] M90                
+TILT=NONE
+1 9000 1 35 1 1 1 0.885 0.885 0.781
+1 1 100
+0 5 10 15 20 25 30 35 40 45 50 55 60 62.5 65 67.5 70 72.5 75 77.5 80 82.5 85 87.5 90 95 105 115 125 135 145 155 165 175 180
+0
+67.00 113.00 238.00 460.00 973.00 1277.00 1388.00 1425.00 1408.00 1338.00 1199.00 1018.00 700.00 430.00 320.00 335.00 227.00 209.00 154.00 137.00 125.00 109.00 91.00 65.00 46.00 38.00 25.00 18.00 10.00 5.00 3.00 3.00 0.00 0.00 0.00

+ 13 - 0
Templates/BaseGame/game/data/Prototyping/iesprofiles/x-arrow.ies

@@ -0,0 +1,13 @@
+IESNA:LM-63-1995
+[TEST] BE1680
+[DATE] 12-FEB-96
+[MANUFAC] LeoMoon Studios
+[LUMCAT] 6340
+[LUMINAIRE]  SURFACE MOUNTED WALL LUMINAIRE 
+[LAMP] (1) 100W A-19 INC
+TILT=NONE
+ 1  1750  1.75  73  1  1  2 -.1  0  .05
+ 1  1  100
+ 0  2.5  5  7.5  10  12.5  15  17.5  20  22.5  25  27.5  30  32.5  35  37.5  40  42.5  45  47.5  50  52.5  55  57.5  60  62.5  65  67.5  70  72.5  75  77.5  80  82.5  85  87.5  90  92.5  95  97.5  100  102.5  105  107.5  110  112.5  115  117.5  120  122.5  125  127.5  130  132.5  135  137.5  140  142.5  145  147.5  150  152.5  155  157.5  160  162.5  165  167.5  170  172.5  175  177.5  180 
+ 0 
+ 167.3  168.9  173  179.9  179.2  151.2  119.4  95.63  81.03  71.95  66.46  62.67  60.25  57.67  52.18  46.62  48.91  63.15  83.15  95.41  97.4  87.75  62.6  43.08  39.26  47.36  52.89  45.74  31.45  18.17  10.5  7.888  8.112  7.592  3.665  .6467  .498  .4252  .3735  .3185  .2765  .2279  .2199  .2021  .1746  .1407  .1358  .1277  .1326  .1342  .1406  .1374  .1358  .1375  .1488  .1488  .1536  .1925  .2183  .2328  .2345  .249  .2765  .3023  .3266  .3476  .3557  .3638  .3541  .3573  .3638  .3719  .3816 

+ 0 - 6
Templates/BaseGame/game/data/Prototyping/shapes/kork_chanShape.asset.taml

@@ -1,6 +0,0 @@
-<ShapeAsset
-    AssetName="kork_chanShape"
-    fileName="@assetFile=kork_chanShape.fbx"
-    constuctorFileName="@assetFile=kork_chanShape.tscript"
-    materialSlot0="@asset=Prototyping:kork_chan_mat"
-    originalFilePath="C:/dev/T3D/PRs/MiscFixes20220525/Templates/BaseGame/game/data/Prototyping/shapes/kork_chanShape.fbx"/>

+ 5 - 25
Templates/BaseGame/game/data/Prototyping/shapes/kork_chanShape.tscript

@@ -1,28 +1,8 @@
-//--- OBJECT WRITE BEGIN ---
-new TSShapeConstructor(kork_chanShape_fbx) {
-   baseShapeAsset = "Prototyping:kork_chanShape";
-   upAxis = "DEFAULT";
-   unit = "-1";
-   LODType = "TrailingNumber";
+
+singleton TSShapeConstructor(kork_chanShapefbx)
+{
+   baseShapeAsset = ":kork_chanShape_shape";
    singleDetailSize = "0";
-   IgnoreNodeScale = "0";
-   AdjustCenter = "0";
-   AdjustFloor = "0";
-   forceUpdateMaterials = "0";
-   convertLeftHanded = "0";
-   calcTangentSpace = "0";
-   genUVCoords = "0";
-   transformUVCoords = "0";
-   flipUVCoords = "1";
-   findInstances = "0";
-   limitBoneWeights = "0";
-   JoinIdenticalVerts = "1";
-   reverseWindingOrder = "1";
-   invertNormals = "0";
-   removeRedundantMats = "1";
-   animTiming = "Seconds";
+   neverImportMat = "DefaultMaterial	ColorEffect*";
    animFPS = "2";
-   canSave = "1";
-   canSaveDynamicFields = "1";
 };
-//--- OBJECT WRITE END ---

+ 5 - 0
Templates/BaseGame/game/data/Prototyping/shapes/kork_chanShape_shape.asset.taml

@@ -0,0 +1,5 @@
+<ShapeAsset
+    AssetName="kork_chanShape_shape"
+    fileName="@assetFile=kork_chanShape.fbx"
+    constuctorFileName="@assetFile=kork_chanShape.tscript"
+    materialSlot0="@asset=Prototyping:kork_chan_mat"/>

+ 1 - 2
Templates/BaseGame/game/data/Prototyping/shapes/kork_chan_image.asset.taml

@@ -1,4 +1,3 @@
 <ImageAsset
     AssetName="kork_chan_image"
-    imageFile="@assetFile=kork_chan.png"
-    originalFilePath="C:/dev/T3D/PRs/MiscFixes20220525/Templates/BaseGame/game/data/Prototyping/shapes/kork_chan.png"/>
+    imageFile="@assetFile=kork_chan.png"/>

+ 3 - 4
Templates/BaseGame/game/data/Prototyping/shapes/kork_chan_mat.asset.taml

@@ -5,12 +5,11 @@
     <Material
         Name="kork_chan_mat"
         mapTo="kork_chan"
-        alphaTest="true"
-        alphaRef="100">
+        doubleSided="true"
+        originalAssetName="kork_chan_mat">
         <Material.Stages>
             <Stages_beginarray
-                DiffuseMapAsset="Prototyping:kork_chan_image"
-                doubleSided="true"/>
+                DiffuseMapAsset="Prototyping:kork_chan_image"/>
         </Material.Stages>
     </Material>
 </MaterialAsset>

+ 3 - 2
Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.tscript

@@ -745,8 +745,9 @@ function MaterialEditorGui::setActiveMaterial( %this, %material )
    MaterialEditorGui.lastMaterial = %material;
    
    // we create or recreate a material to hold in a pristine state
-   if(isObject(notDirtyMaterial))
-      notDirtyMaterial.delete();
+   // or, this crashes the ap. fix properly - BJR
+//   if(isObject(notDirtyMaterial))
+//      notDirtyMaterial.delete();
       
    singleton Material(notDirtyMaterial)
    {