Merge remote-tracking branch 'upstream/development' into AssimpLoaader-Fix

This commit is contained in:
marauder2k7 2024-02-22 10:01:32 +00:00
commit 1ccf4cff85
42 changed files with 1294 additions and 84 deletions

View file

@ -79,7 +79,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

View file

@ -33,6 +33,8 @@
#include "core/resourceManager.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "renderInstance/renderProbeMgr.h"
#include "T3D/lighting/skylight.h"
// GuiMaterialPreview
GuiMaterialPreview::GuiMaterialPreview()
@ -372,6 +374,9 @@ void GuiMaterialPreview::renderWorld(const RectI &updateRect)
FogData savedFogData = gClientSceneGraph->getFogData();
gClientSceneGraph->setFogData( FogData() ); // no fog in preview window
if (Skylight::smSkylightProbe.isValid())
PROBEMGR->submitProbe(Skylight::smSkylightProbe->getProbeInfo());
RenderPassManager* renderPass = gClientSceneGraph->getDefaultRenderPass();
SceneRenderState state
(

View file

@ -31,6 +31,8 @@
#include "math/mathTypes.h"
#include "gfx/gfxTransformSaver.h"
#include "console/engineAPI.h"
#include "renderInstance/renderProbeMgr.h"
#include "T3D/lighting/skylight.h"
IMPLEMENT_CONOBJECT( GuiObjectView );
@ -541,6 +543,12 @@ void GuiObjectView::renderWorld( const RectI& updateRect )
// Render primary model.
if (Skylight::smSkylightProbe.isValid())
PROBEMGR->submitProbe(Skylight::smSkylightProbe->getProbeInfo());
FogData savedFogData = gClientSceneGraph->getFogData();
gClientSceneGraph->setFogData(FogData()); // no fog in preview window
if(mModelInstance)
{
if( mRunThread )
@ -567,6 +575,7 @@ void GuiObjectView::renderWorld( const RectI& updateRect )
renderPass->renderPass( &state );
gClientSceneGraph->setFogData(savedFogData); // restore fog setting
// Make sure to remove our fake sun.
LIGHTMGR->unregisterAllLights();
}

View file

@ -378,6 +378,7 @@ public:
/// Invokes a cubemap bake action for this probe
/// </summary>
void bake();
ProbeInfo* getProbeInfo() { return &mProbeInfo; }
};
typedef ReflectionProbe::ProbeInfo::ProbeShapeType ReflectProbeType;

View file

@ -59,6 +59,8 @@ extern bool gEditingMission;
extern ColorI gCanvasClearColor;
bool Skylight::smRenderSkylights = true;
SimObjectPtr<Skylight> Skylight::smSkylightProbe = nullptr;
IMPLEMENT_CO_NETOBJECT_V1(Skylight);
ConsoleDocClass(Skylight,
@ -117,6 +119,8 @@ bool Skylight::onAdd()
if (!Parent::onAdd())
return false;
if (isClientObject())
smSkylightProbe = this;
return true;
}

View file

@ -108,6 +108,7 @@ public:
void prepRenderImage(SceneRenderState *state);
void setPreviewMatParameters(SceneRenderState* renderState, BaseMatInstance* mat);
static SimObjectPtr<Skylight> smSkylightProbe;
};
#endif // _Skylight_H_

View file

@ -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);
};

View file

@ -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,6 +95,103 @@ 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);
@ -142,21 +241,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);

View file

@ -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());
}

View file

@ -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

View file

@ -54,6 +54,7 @@ GuiInspector::GuiInspector()
mForcedArrayIndex(-1)
{
mPadding = 1;
mSearchText = StringTable->EmptyString();
}
//-----------------------------------------------------------------------------
@ -79,7 +80,8 @@ void GuiInspector::initPersistFields()
"If false the custom fields Name, Id, and Source Class will not be shown." );
addField("forcedArrayIndex", TypeS32, Offset(mForcedArrayIndex, GuiInspector));
addField("searchText", TypeString, Offset(mSearchText, GuiInspector), "A string that, if not blank, is used to filter shown fields");
endGroup( "Inspector" );
Parent::initPersistFields();
@ -829,6 +831,12 @@ void GuiInspector::setForcedArrayIndex(S32 arrayIndex)
refresh();
}
void GuiInspector::setSearchText(StringTableEntry searchText)
{
mSearchText = searchText;
refresh();
}
//=============================================================================
// Console Methods.
//=============================================================================
@ -1000,3 +1008,10 @@ DefineEngineMethod(GuiInspector, setForcedArrayIndex, void, (S32 arrayIndex), (-
{
object->setForcedArrayIndex(arrayIndex);
}
DefineEngineMethod(GuiInspector, setSearchText, void, (const char* searchText), (""),
"Sets the searched text used to filter out displayed fields in the inspector."
"@param searchText The text to be used as a filter for field names. Leave as blank to clear search")
{
object->setSearchText(searchText);
}

View file

@ -171,6 +171,10 @@ public:
void setForcedArrayIndex(S32 arrayIndex);
StringTableEntry getSearchText() { return mSearchText; }
void setSearchText(StringTableEntry searchText);
protected:
typedef Vector< SimObjectPtr< SimObject > > TargetVector;
@ -190,6 +194,8 @@ protected:
String mGroupFilters;
bool mShowCustomFields;
S32 mForcedArrayIndex;
StringTableEntry mSearchText;
};
#endif

View file

@ -37,6 +37,8 @@
#include "T3D/assets/ShapeAsset.h"
#include "T3D/assets/ShapeAnimationAsset.h"
#include "renderInstance/renderProbeMgr.h"
#include "T3D/lighting/skylight.h"
#ifdef TORQUE_COLLADA
#include "collision/optimizedPolyList.h"
@ -1409,6 +1411,8 @@ void GuiShapeEdPreview::renderWorld(const RectI &updateRect)
FogData savedFogData = gClientSceneGraph->getFogData();
gClientSceneGraph->setFogData( FogData() ); // no fog in preview window
if (Skylight::smSkylightProbe.isValid())
PROBEMGR->submitProbe(Skylight::smSkylightProbe->getProbeInfo());
SceneRenderState state
(
gClientSceneGraph,

View file

@ -175,6 +175,9 @@ GuiControl* GuiInspectorDatablockField::constructEditControl()
//Add add button
mAddButton = new GuiBitmapButtonCtrl();
if(mDesiredClass == NULL)
return retCtrl;
dSprintf(szBuffer, sizeof(szBuffer), "DatablockEditorPlugin.createNewDatablockOfType(%s, %d.getText());", mDesiredClass->getClassName(), retCtrl->getId());
mAddButton->setField("Command", szBuffer);

View file

@ -123,6 +123,12 @@ bool GuiInspectorDynamicGroup::inspectGroup()
SimFieldDictionary * fieldDictionary = target->getFieldDictionary();
for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
{
String searchText = mParent->getSearchText();
if (searchText != String::EmptyString) {
if (String((*ditr)->slotName).find(searchText, 0, String::NoCase | String::Left) == String::NPos)
continue;
}
if( i == 0 )
{
flist.increment();

View file

@ -288,6 +288,12 @@ bool GuiInspectorGroup::inspectGroup()
if (field->flag.test(AbstractClassRep::FIELD_HideInInspectors))
continue;
String searchText = mParent->getSearchText();
if (searchText != String::EmptyString) {
if (String(field->pFieldname).find(searchText, 0, String::NoCase | String::Left) == String::NPos)
continue;
}
if ((bGrabItems == true || (bNoGroup == true && bGrabItems == false)) && itr->type != AbstractClassRep::DeprecatedFieldType)
{
if (bNoGroup == true && bGrabItems == true)

View file

@ -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 );
else
vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_None, false );
vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_None );
// 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;
}

View file

@ -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();

View file

@ -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;
}

View file

@ -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 );

View file

@ -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;
/// @}

View file

@ -94,6 +94,7 @@ public:
Misc,
DynamicLight,
DynamicLightMask,
PhotometricMask,
NormalizeCube,
TexTarget,
AccuMap,

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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);