summaryrefslogtreecommitdiffstats
path: root/src/runtimerender/resourcemanager
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtimerender/resourcemanager')
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.cpp326
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.h85
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp1092
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h119
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp538
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h96
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp715
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h184
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp1262
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp695
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.h88
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureFreeImageCompat.h413
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp851
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureHDR.cpp255
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.cpp277
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.h39
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.cpp599
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.h129
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.cpp101
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.h96
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.cpp436
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.h81
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.cpp174
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.h126
24 files changed, 8777 insertions, 0 deletions
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.cpp
new file mode 100644
index 0000000..963186f
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.cpp
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderBufferLoader.h"
+#include "foundation/Qt3DSInvasiveLinkedList.h"
+#include "foundation/Qt3DSMutex.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSSync.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "Qt3DSRenderThreadPool.h"
+
+using namespace qt3ds::render;
+
+namespace {
+struct SBufferLoader;
+struct SBufferLoadResult : public ILoadedBuffer
+{
+ NVFoundationBase &m_Foundation;
+ CRegisteredString m_Path;
+ IBufferLoaderCallback *m_UserData;
+ NVDataRef<QT3DSU8> m_Data;
+ QT3DSI32 mRefCount;
+
+ SBufferLoadResult(NVFoundationBase &fnd, CRegisteredString p, IBufferLoaderCallback *ud,
+ NVDataRef<QT3DSU8> data)
+ : m_Foundation(fnd)
+ , m_Path(p)
+ , m_UserData(ud)
+ , m_Data(data)
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator())
+
+ CRegisteredString Path() override { return m_Path; }
+ // Data is released when the buffer itself is released.
+ NVDataRef<QT3DSU8> Data() override { return m_Data; }
+ IBufferLoaderCallback *UserData() override { return m_UserData; }
+
+ static SBufferLoadResult *Allocate(QT3DSU32 inBufferSize, NVFoundationBase &fnd,
+ CRegisteredString p,
+ NVScopedRefCounted<IBufferLoaderCallback> ud)
+ {
+ size_t allocSize = sizeof(SBufferLoadResult) + inBufferSize;
+ QT3DSU8 *allocMem =
+ (QT3DSU8 *)fnd.getAllocator().allocate(allocSize, "ILoadedBuffer", __FILE__, __LINE__);
+ if (allocMem == NULL)
+ return NULL;
+ QT3DSU8 *bufferStart = allocMem + sizeof(SBufferLoadResult);
+ NVDataRef<QT3DSU8> dataBuffer = toDataRef(bufferStart, inBufferSize);
+ return new (allocMem) SBufferLoadResult(fnd, p, ud, dataBuffer);
+ }
+};
+struct SLoadedBufferImpl
+{
+ SBufferLoader &m_Loader;
+ QT3DSU64 m_JobId;
+ QT3DSU64 m_LoadId;
+ CRegisteredString m_Path;
+ NVScopedRefCounted<IBufferLoaderCallback> m_UserData;
+ bool m_Quiet;
+ volatile bool m_Cancel;
+ NVScopedRefCounted<SBufferLoadResult> m_Result;
+ SLoadedBufferImpl *m_NextBuffer;
+ SLoadedBufferImpl *m_PreviousBuffer;
+
+ SLoadedBufferImpl(SBufferLoader &l, CRegisteredString inPath,
+ NVScopedRefCounted<IBufferLoaderCallback> ud, bool inQuiet, QT3DSU64 loadId)
+ : m_Loader(l)
+ , m_JobId(0)
+ , m_LoadId(loadId)
+ , m_Path(inPath)
+ , m_UserData(ud)
+ , m_Quiet(inQuiet)
+ , m_Cancel(false)
+ , m_NextBuffer(NULL)
+ , m_PreviousBuffer(NULL)
+ {
+ }
+};
+
+DEFINE_INVASIVE_LIST(LoadedBufferImpl);
+IMPLEMENT_INVASIVE_LIST(LoadedBufferImpl, m_PreviousBuffer, m_NextBuffer);
+
+struct SBufferLoader : public IBufferLoader
+{
+ NVFoundationBase &m_Foundation;
+ NVScopedRefCounted<IInputStreamFactory> m_Factory;
+ NVScopedRefCounted<IThreadPool> m_ThreadPool;
+
+ Mutex m_BuffersToLoadMutex;
+ TLoadedBufferImplList m_BuffersToLoad;
+
+ Mutex m_BuffersLoadingMutex;
+ TLoadedBufferImplList m_BuffersLoading;
+
+ Mutex m_LoadedBuffersMutex;
+ TLoadedBufferImplList m_LoadedBuffers;
+
+ Sync m_BufferLoadedEvent;
+
+ QT3DSU64 m_NextBufferId;
+
+ QT3DSI32 mRefCount;
+
+ SBufferLoader(NVFoundationBase &fnd, IInputStreamFactory &fac, IThreadPool &tp)
+ : m_Foundation(fnd)
+ , m_Factory(fac)
+ , m_ThreadPool(tp)
+ , m_BuffersToLoadMutex(fnd.getAllocator())
+ , m_BuffersLoadingMutex(fnd.getAllocator())
+ , m_LoadedBuffersMutex(fnd.getAllocator())
+ , m_BufferLoadedEvent(fnd.getAllocator())
+ , m_NextBufferId(1)
+ , mRefCount(0)
+ {
+ m_BufferLoadedEvent.reset();
+ }
+
+ virtual ~SBufferLoader()
+ {
+ {
+ Mutex::ScopedLock __locker(m_BuffersToLoadMutex);
+ for (TLoadedBufferImplList::iterator iter = m_BuffersToLoad.begin(),
+ end = m_BuffersToLoad.end();
+ iter != end; ++iter) {
+ m_ThreadPool->CancelTask(iter->m_JobId);
+ }
+ }
+
+ // Pull any remaining buffers out of the thread system.
+ while (WillLoadedBuffersBeAvailable()) {
+ NextLoadedBuffer();
+ }
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ static void InitializeActiveLoadingBuffer(SLoadedBufferImpl &theBuffer)
+ {
+ Mutex::ScopedLock theLocker(theBuffer.m_Loader.m_BuffersToLoadMutex);
+ Mutex::ScopedLock theSecondLocker(theBuffer.m_Loader.m_BuffersLoadingMutex);
+ theBuffer.m_Loader.m_BuffersToLoad.remove(theBuffer);
+ theBuffer.m_Loader.m_BuffersLoading.push_back(theBuffer);
+ }
+
+ static void SetBufferAsLoaded(SLoadedBufferImpl &theBuffer)
+ {
+ Mutex::ScopedLock theSecondLocker(theBuffer.m_Loader.m_BuffersLoadingMutex);
+ Mutex::ScopedLock theLocker(theBuffer.m_Loader.m_LoadedBuffersMutex);
+ theBuffer.m_Loader.m_BuffersLoading.remove(theBuffer);
+ theBuffer.m_Loader.m_LoadedBuffers.push_back(theBuffer);
+ theBuffer.m_Loader.m_BufferLoadedEvent.set();
+ theBuffer.m_Loader.m_BufferLoadedEvent.reset();
+ }
+
+ static void LoadNextBuffer(void *loader)
+ {
+ SLoadedBufferImpl &theBuffer = *reinterpret_cast<SLoadedBufferImpl *>(loader);
+
+ InitializeActiveLoadingBuffer(theBuffer);
+ NVScopedRefCounted<IRefCountedInputStream> theStream =
+ theBuffer.m_Loader.m_Factory->GetStreamForFile(theBuffer.m_Path.c_str(),
+ theBuffer.m_Quiet);
+ if (theStream && theBuffer.m_Cancel == false) {
+ theStream->SetPosition(0, SeekPosition::End);
+ QT3DSI64 theFileLen = theStream->GetPosition();
+ if (theFileLen > 0 && theFileLen < (QT3DSU32)QT3DS_MAX_U32) {
+ QT3DSU32 required = (QT3DSU32)theFileLen;
+ theBuffer.m_Result =
+ SBufferLoadResult::Allocate(required, theBuffer.m_Loader.m_Foundation,
+ theBuffer.m_Path, theBuffer.m_UserData);
+ QT3DSU32 amountRead = 0;
+ QT3DSU32 total = amountRead;
+ if (theBuffer.m_Result && theBuffer.m_Cancel == false) {
+ NVDataRef<QT3DSU8> theDataBuffer(theBuffer.m_Result->m_Data);
+ theStream->SetPosition(0, SeekPosition::Begin);
+ amountRead = theStream->Read(theDataBuffer);
+ total += amountRead;
+ // Ensure we keep trying, not all file systems allow huge reads.
+ while (total < required && amountRead > 0 && theBuffer.m_Cancel == false) {
+ NVDataRef<QT3DSU8> newBuffer(theDataBuffer.mData + total, required - total);
+ amountRead = theStream->Read(newBuffer);
+ total += amountRead;
+ }
+ }
+ if (theBuffer.m_Cancel || total != required) {
+ theBuffer.m_Result->m_Data = NVDataRef<QT3DSU8>();
+ }
+ }
+ }
+
+ // only callback if the file was successfully loaded.
+ if (theBuffer.m_UserData) {
+ if (theBuffer.m_Cancel == false && theBuffer.m_Result.mPtr
+ && theBuffer.m_Result->m_Data.size()) {
+ theBuffer.m_UserData->OnBufferLoaded(*theBuffer.m_Result.mPtr);
+ } else {
+ if (theBuffer.m_Cancel)
+ theBuffer.m_UserData->OnBufferLoadCancelled(theBuffer.m_Path);
+ else
+ theBuffer.m_UserData->OnBufferLoadFailed(theBuffer.m_Path);
+ }
+ }
+
+ SetBufferAsLoaded(theBuffer);
+ }
+ static void CancelNextBuffer(void *loader)
+ {
+ SLoadedBufferImpl &theBuffer = *reinterpret_cast<SLoadedBufferImpl *>(loader);
+ theBuffer.m_Cancel = true;
+ InitializeActiveLoadingBuffer(theBuffer);
+
+ if (theBuffer.m_UserData)
+ theBuffer.m_UserData->OnBufferLoadCancelled(theBuffer.m_Path);
+
+ SetBufferAsLoaded(theBuffer);
+ }
+
+ // nonblocking. Quiet failure is passed to the input stream factory.
+ QT3DSU64 QueueForLoading(CRegisteredString inPath,
+ IBufferLoaderCallback *inUserData = NULL,
+ bool inQuietFailure = false) override
+ {
+ SLoadedBufferImpl &theBuffer = *QT3DS_NEW(m_Foundation.getAllocator(), SLoadedBufferImpl)(
+ *this, inPath, inUserData, inQuietFailure, m_NextBufferId);
+ ++m_NextBufferId;
+ {
+ Mutex::ScopedLock theLocker(m_BuffersToLoadMutex);
+ m_BuffersToLoad.push_back(theBuffer);
+ }
+ theBuffer.m_JobId = m_ThreadPool->AddTask(&theBuffer, LoadNextBuffer, CancelNextBuffer);
+ return theBuffer.m_LoadId;
+ }
+
+ void CancelBufferLoad(QT3DSU64 inBufferId) override
+ {
+ {
+ Mutex::ScopedLock theLocker(m_BuffersToLoadMutex);
+ SLoadedBufferImpl *theLoadedBuffer = NULL;
+ for (TLoadedBufferImplList::iterator iter = m_BuffersToLoad.begin(),
+ end = m_BuffersToLoad.end();
+ iter != end && theLoadedBuffer == NULL; ++iter) {
+ if (iter->m_LoadId == inBufferId) {
+ theLoadedBuffer = &(*iter);
+ // both cancellation attempts are necessary. The user will still get
+ // a load result, it will just have no data.
+ theLoadedBuffer->m_Cancel = true;
+ m_ThreadPool->CancelTask(theLoadedBuffer->m_JobId);
+ }
+ }
+ }
+ }
+
+ // If we were will to wait, will we ever get another buffer
+ bool WillLoadedBuffersBeAvailable() override
+ {
+ Mutex::ScopedLock theLocker(m_BuffersToLoadMutex);
+ Mutex::ScopedLock theSecondLocker(m_BuffersLoadingMutex);
+ return AreLoadedBuffersAvailable() || m_BuffersToLoad.empty() == false
+ || m_BuffersLoading.empty() == false;
+ }
+ // Will nextLoadedBuffer block or not?
+ bool AreLoadedBuffersAvailable() override
+ {
+ Mutex::ScopedLock theLocker(m_LoadedBuffersMutex);
+ return m_LoadedBuffers.empty() == false;
+ }
+
+ // blocking, be careful with this. No order guarantees here.
+ NVScopedRefCounted<ILoadedBuffer> NextLoadedBuffer() override
+ {
+ while (!AreLoadedBuffersAvailable()) {
+ m_BufferLoadedEvent.wait();
+ }
+ SLoadedBufferImpl *theBuffer;
+ {
+ Mutex::ScopedLock theLocker(m_LoadedBuffersMutex);
+ theBuffer = m_LoadedBuffers.back_ptr();
+ m_LoadedBuffers.remove(*theBuffer);
+ }
+ NVScopedRefCounted<ILoadedBuffer> retval(theBuffer->m_Result);
+ if (retval.mPtr == NULL) {
+ retval = SBufferLoadResult::Allocate(0, m_Foundation, theBuffer->m_Path,
+ theBuffer->m_UserData);
+ }
+ NVDelete(m_Foundation.getAllocator(), theBuffer);
+ return retval;
+ }
+};
+}
+
+IBufferLoader &IBufferLoader::Create(NVFoundationBase &fnd, IInputStreamFactory &inFactory,
+ IThreadPool &inThreadPool)
+{
+ return *QT3DS_NEW(fnd.getAllocator(), SBufferLoader)(fnd, inFactory, inThreadPool);
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.h b/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.h
new file mode 100644
index 0000000..8d75e25
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferLoader.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_RENDER_BUFFER_LOADED_H
+#define QT3DS_RENDER_BUFFER_LOADED_H
+
+#include "Qt3DSRender.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/StringTable.h"
+
+namespace qt3ds {
+namespace render {
+
+ class IBufferLoaderCallback;
+
+ class ILoadedBuffer : public NVRefCounted
+ {
+ public:
+ virtual CRegisteredString Path() = 0;
+ // Data is released when the buffer itself is released.
+ virtual NVDataRef<QT3DSU8> Data() = 0;
+ virtual IBufferLoaderCallback *UserData() = 0;
+ };
+
+ class IBufferLoaderCallback : public NVRefCounted
+ {
+ public:
+ virtual void OnBufferLoaded(ILoadedBuffer &inBuffer) = 0;
+ virtual void OnBufferLoadFailed(CRegisteredString inPath) = 0;
+ virtual void OnBufferLoadCancelled(CRegisteredString inPath) = 0;
+ };
+
+ // Job of this object is to load buffers all the way to memory as fast as possible.
+ class IBufferLoader : public NVRefCounted
+ {
+ public:
+ // nonblocking. Quiet failure is passed to the input stream factory.
+ // Returns handle to loading buffer
+ virtual QT3DSU64 QueueForLoading(CRegisteredString inPath,
+ IBufferLoaderCallback *inUserData = NULL,
+ bool inQuietFailure = false) = 0;
+ // Cancel a buffer that has not made it to the loaded buffers list.
+ virtual void CancelBufferLoad(QT3DSU64 inBufferId) = 0;
+ // If we were will to wait, will we ever get another buffer
+ virtual bool WillLoadedBuffersBeAvailable() = 0;
+ // Will nextLoadedBuffer block or not?
+ virtual bool AreLoadedBuffersAvailable() = 0;
+
+ // blocking, be careful with this. No guarantees about timely return here.
+ virtual NVScopedRefCounted<ILoadedBuffer> NextLoadedBuffer() = 0;
+
+ static IBufferLoader &Create(NVFoundationBase &fnd, IInputStreamFactory &inFactory,
+ IThreadPool &inThreadPool);
+ };
+}
+}
+
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp
new file mode 100644
index 0000000..eb23f3d
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp
@@ -0,0 +1,1092 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifdef _WIN32
+#pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
+#endif
+#include "Qt3DSRender.h"
+#include "Qt3DSRenderBufferManager.h"
+#include "EASTL/string.h"
+#include "foundation/Qt3DSAllocator.h"
+#include "render/Qt3DSRenderContext.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "EASTL/hash_map.h"
+#include "foundation/FileTools.h"
+#include "Qt3DSImportMesh.h"
+#include "Qt3DSRenderMesh.h"
+#include "foundation/Qt3DSAllocatorCallback.h"
+#include "Qt3DSRenderLoadedTexture.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "Qt3DSRenderImageScaler.h"
+#include "Qt3DSRenderImage.h"
+#include "Qt3DSTextRenderer.h"
+#include "foundation/Qt3DSPerfTimer.h"
+#include "foundation/Qt3DSMutex.h"
+#include "Qt3DSRenderPrefilterTexture.h"
+#include <QtCore/qdir.h>
+
+using namespace qt3ds::render;
+
+namespace {
+
+using eastl::hash;
+using eastl::pair;
+using eastl::make_pair;
+typedef eastl::basic_string<char8_t, ForwardingAllocator> TStr;
+struct StrHasher
+{
+ size_t operator()(const TStr &str) const
+ {
+ return hash<const char8_t *>()((const char8_t *)str.c_str());
+ }
+};
+
+struct StrEq
+{
+ bool operator()(const TStr &lhs, const TStr &rhs) const { return lhs == rhs; }
+};
+
+struct SImageEntry : public SImageTextureData
+{
+ bool m_Loaded;
+ SImageEntry()
+ : SImageTextureData(), m_Loaded(false)
+ {
+ }
+ SImageEntry(const SImageEntry &entry)
+ : SImageTextureData(entry), m_Loaded(entry.m_Loaded)
+ {
+
+ }
+};
+
+struct SPrimitiveEntry
+{
+ // Name of the primitive as it will be in the UIP file
+ CRegisteredString m_PrimitiveName;
+ // Name of the primitive file on the filesystem
+ CRegisteredString m_FileName;
+};
+
+struct SBufferManager : public IBufferManager
+{
+ typedef eastl::hash_set<CRegisteredString, eastl::hash<CRegisteredString>,
+ eastl::equal_to<CRegisteredString>, ForwardingAllocator>
+ TStringSet;
+ typedef nvhash_map<CRegisteredString, SImageEntry> TImageMap;
+ typedef nvhash_map<CRegisteredString, SRenderMesh *> TMeshMap;
+ typedef nvhash_map<CRegisteredString, CRegisteredString> TAliasImageMap;
+
+ NVScopedRefCounted<NVRenderContext> m_Context;
+ NVScopedRefCounted<IStringTable> m_StrTable;
+ NVScopedRefCounted<IInputStreamFactory> m_InputStreamFactory;
+ IPerfTimer &m_PerfTimer;
+ volatile QT3DSI32 mRefCount;
+ TStr m_PathBuilder;
+ TImageMap m_ImageMap;
+ Mutex m_LoadedImageSetMutex;
+ TStringSet m_LoadedImageSet;
+ TAliasImageMap m_AliasImageMap;
+ TMeshMap m_MeshMap;
+ SPrimitiveEntry m_PrimitiveNames[5];
+ nvvector<qt3ds::render::NVRenderVertexBufferEntry> m_EntryBuffer;
+ bool m_GPUSupportsDXT;
+ bool m_reloadableResources;
+
+ QHash<QString, ReloadableTexturePtr> m_reloadableTextures;
+
+ static const char8_t *GetPrimitivesDirectory() { return "res//primitives"; }
+
+ SBufferManager(NVRenderContext &ctx, IStringTable &strTable,
+ IInputStreamFactory &inInputStreamFactory, IPerfTimer &inTimer)
+ : m_Context(ctx)
+ , m_StrTable(strTable)
+ , m_InputStreamFactory(inInputStreamFactory)
+ , m_PerfTimer(inTimer)
+ , mRefCount(0)
+ , m_PathBuilder(ForwardingAllocator(ctx.GetAllocator(), "SBufferManager::m_PathBuilder"))
+ , m_ImageMap(ctx.GetAllocator(), "SBufferManager::m_ImageMap")
+ , m_LoadedImageSetMutex(ctx.GetAllocator())
+ , m_LoadedImageSet(
+ ForwardingAllocator(ctx.GetAllocator(), "SBufferManager::m_LoadedImageSet"))
+ , m_AliasImageMap(ctx.GetAllocator(), "SBufferManager::m_AliasImageMap")
+ , m_MeshMap(ctx.GetAllocator(), "SBufferManager::m_MeshMap")
+ , m_EntryBuffer(ctx.GetAllocator(), "SBufferManager::m_EntryBuffer")
+ , m_GPUSupportsDXT(ctx.AreDXTImagesSupported())
+ , m_reloadableResources(false)
+ {
+ }
+ virtual ~SBufferManager() { Clear(); }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Context->GetAllocator())
+
+ CRegisteredString CombineBaseAndRelative(const char8_t *inBase,
+ const char8_t *inRelative) override
+ {
+ CFileTools::CombineBaseAndRelative(inBase, inRelative, m_PathBuilder);
+ return m_StrTable->RegisterStr(m_PathBuilder.c_str());
+ }
+
+ void SetImageHasTransparency(CRegisteredString inImagePath, bool inHasTransparency) override
+ {
+ pair<TImageMap::iterator, bool> theImage =
+ m_ImageMap.insert(make_pair(inImagePath, SImageEntry()));
+ theImage.first->second.m_TextureFlags.SetHasTransparency(inHasTransparency);
+ }
+
+ bool GetImageHasTransparency(CRegisteredString inSourcePath) const override
+ {
+ TImageMap::const_iterator theIter = m_ImageMap.find(inSourcePath);
+ if (theIter != m_ImageMap.end())
+ return theIter->second.m_TextureFlags.HasTransparency();
+ return false;
+ }
+
+ void SetImageTransparencyToFalseIfNotSet(CRegisteredString inSourcePath) override
+ {
+ pair<TImageMap::iterator, bool> theImage =
+ m_ImageMap.insert(make_pair(inSourcePath, SImageEntry()));
+ // If we did actually insert something
+ if (theImage.second)
+ theImage.first->second.m_TextureFlags.SetHasTransparency(false);
+ }
+
+ void SetInvertImageUVCoords(CRegisteredString inImagePath, bool inShouldInvertCoords) override
+ {
+ pair<TImageMap::iterator, bool> theImage =
+ m_ImageMap.insert(make_pair(inImagePath, SImageEntry()));
+ theImage.first->second.m_TextureFlags.SetInvertUVCoords(inShouldInvertCoords);
+ }
+
+ bool IsImageLoaded(CRegisteredString inSourcePath) override
+ {
+ Mutex::ScopedLock __locker(m_LoadedImageSetMutex);
+ return m_LoadedImageSet.find(inSourcePath) != m_LoadedImageSet.end();
+ }
+
+ bool AliasImagePath(CRegisteredString inSourcePath, CRegisteredString inAliasPath,
+ bool inIgnoreIfLoaded) override
+ {
+ if (inSourcePath.IsValid() == false || inAliasPath.IsValid() == false)
+ return false;
+ // If the image is loaded then we ignore this call in some cases.
+ if (inIgnoreIfLoaded && IsImageLoaded(inSourcePath))
+ return false;
+ m_AliasImageMap.insert(eastl::make_pair(inSourcePath, inAliasPath));
+ return true;
+ }
+
+ void UnaliasImagePath(CRegisteredString inSourcePath) override
+ {
+ m_AliasImageMap.erase(inSourcePath);
+ }
+
+ CRegisteredString GetImagePath(CRegisteredString inSourcePath) override
+ {
+ TAliasImageMap::iterator theAliasIter = m_AliasImageMap.find(inSourcePath);
+ if (theAliasIter != m_AliasImageMap.end())
+ return theAliasIter->second;
+ return inSourcePath;
+ }
+
+ CRegisteredString getImagePath(const QString &path)
+ {
+ TAliasImageMap::iterator theAliasIter
+ = m_AliasImageMap.find(m_StrTable->RegisterStr(qPrintable(path)));
+ if (theAliasIter != m_AliasImageMap.end())
+ return theAliasIter->second;
+ return m_StrTable->RegisterStr(qPrintable(path));
+ }
+
+ static inline int wrapMod(int a, int base)
+ {
+ int ret = a % base;
+ if (ret < 0)
+ ret += base;
+ return ret;
+ }
+
+ static inline void getWrappedCoords(int &sX, int &sY, int width, int height)
+ {
+ if (sY < 0) {
+ sX -= width >> 1;
+ sY = -sY;
+ }
+ if (sY >= height) {
+ sX += width >> 1;
+ sY = height - sY;
+ }
+ sX = wrapMod(sX, width);
+ sY = wrapMod(sY, height);
+ }
+
+ template <typename V, typename C>
+ void iterateAll(const V &vv, C c)
+ {
+ for (const auto x : vv)
+ c(x);
+ }
+
+ void loadTextureImage(SReloadableImageTextureData &data)
+ {
+ CRegisteredString imagePath = getImagePath(data.m_path);
+ TImageMap::iterator theIter = m_ImageMap.find(imagePath);
+ if ((theIter == m_ImageMap.end() || theIter->second.m_Loaded == false)
+ && imagePath.IsValid()) {
+ NVScopedReleasable<SLoadedTexture> theLoadedImage;
+ SImageTextureData textureData;
+
+ doImageLoad(imagePath, theLoadedImage);
+
+ if (theLoadedImage) {
+ textureData = LoadRenderImage(imagePath, *theLoadedImage, data.m_scanTransparency,
+ data.m_bsdfMipmap);
+ data.m_Texture = textureData.m_Texture;
+ data.m_TextureFlags = textureData.m_TextureFlags;
+ data.m_BSDFMipMap = textureData.m_BSDFMipMap;
+ data.m_loaded = true;
+ iterateAll(data.m_callbacks, [](SImage *img){ img->m_Flags.SetDirty(true); });
+ } else {
+ // We want to make sure that bad path fails once and doesn't fail over and over
+ // again which could slow down the system quite a bit.
+ pair<TImageMap::iterator, bool> theImage =
+ m_ImageMap.insert(make_pair(imagePath, SImageEntry()));
+ theImage.first->second.m_Loaded = true;
+ qCWarning(WARNING, "Failed to load image: %s", imagePath.c_str());
+ theIter = theImage.first;
+ }
+ } else {
+ SImageEntry textureData = theIter->second;
+ if (textureData.m_Loaded) {
+ data.m_Texture = textureData.m_Texture;
+ data.m_TextureFlags = textureData.m_TextureFlags;
+ data.m_BSDFMipMap = textureData.m_BSDFMipMap;
+ data.m_loaded = true;
+ iterateAll(data.m_callbacks, [](SImage *img){ img->m_Flags.SetDirty(true); });
+ }
+ }
+ }
+
+ void unloadTextureImage(SReloadableImageTextureData &data)
+ {
+ CRegisteredString r = m_StrTable->RegisterStr(qPrintable(data.m_path));
+ data.m_loaded = false;
+ data.m_Texture = nullptr;
+ data.m_BSDFMipMap = nullptr;
+ data.m_TextureFlags = {};
+ iterateAll(data.m_callbacks, [](SImage *img){ img->m_Flags.SetDirty(true); });
+ InvalidateBuffer(r);
+ }
+
+ void loadSet(const QSet<QString> &imageSet) override
+ {
+ for (const auto &x : imageSet) {
+ if (!m_reloadableTextures.contains(x)) {
+ auto img = CreateReloadableImage(m_StrTable->RegisterStr(qPrintable(x)), false,
+ false);
+ img->m_initialized = false;
+ loadTextureImage(*m_reloadableTextures[x]);
+ } else if (!m_reloadableTextures[x]->m_loaded) {
+ loadTextureImage(*m_reloadableTextures[x]);
+ }
+ }
+ }
+
+ void unloadSet(const QSet<QString> &imageSet) override
+ {
+ for (const auto &x : imageSet) {
+ if (m_reloadableTextures.contains(x)) {
+ if (m_reloadableTextures[x]->m_loaded)
+ unloadTextureImage(*m_reloadableTextures[x]);
+ }
+ }
+ }
+
+ virtual ReloadableTexturePtr CreateReloadableImage(CRegisteredString inSourcePath,
+ bool inForceScanForTransparency,
+ bool inBsdfMipmaps) override
+ {
+ QString path = QString::fromLatin1(inSourcePath.c_str());
+ const bool inserted = m_reloadableTextures.contains(path);
+ if (!inserted || (inserted && m_reloadableTextures[path]->m_initialized == false)) {
+ if (!inserted)
+ m_reloadableTextures.insert(path, ReloadableTexturePtr::create());
+ m_reloadableTextures[path]->m_path = path;
+ m_reloadableTextures[path]->m_scanTransparency = inForceScanForTransparency;
+ m_reloadableTextures[path]->m_bsdfMipmap = inBsdfMipmaps;
+ m_reloadableTextures[path]->m_initialized = true;
+
+ if (!m_reloadableResources)
+ loadTextureImage(*m_reloadableTextures[path]);
+
+ CRegisteredString imagePath = getImagePath(path);
+ TImageMap::iterator theIter = m_ImageMap.find(imagePath);
+ if (theIter != m_ImageMap.end()) {
+ SImageEntry textureData = theIter->second;
+ if (textureData.m_Loaded) {
+ m_reloadableTextures[path]->m_Texture = textureData.m_Texture;
+ m_reloadableTextures[path]->m_TextureFlags = textureData.m_TextureFlags;
+ m_reloadableTextures[path]->m_BSDFMipMap = textureData.m_BSDFMipMap;
+ m_reloadableTextures[path]->m_loaded = true;
+ }
+ }
+ }
+ return m_reloadableTextures[path];
+ }
+
+ void doImageLoad(CRegisteredString inImagePath,
+ NVScopedReleasable<SLoadedTexture> &theLoadedImage)
+ {
+ SStackPerfTimer __perfTimer(m_PerfTimer, "Image Decompression");
+ theLoadedImage = SLoadedTexture::Load(
+ inImagePath.c_str(), m_Context->GetFoundation(), *m_InputStreamFactory,
+ true, m_Context->GetRenderContextType());
+ // Hackish solution to custom materials not finding their textures if they are used
+ // in sub-presentations.
+ if (!theLoadedImage) {
+ if (QDir(inImagePath.c_str()).isRelative()) {
+ QString searchPath = inImagePath.c_str();
+ if (searchPath.startsWith(QLatin1String("./")))
+ searchPath.prepend(QLatin1Char('.'));
+ int loops = 0;
+ while (!theLoadedImage && ++loops <= 3) {
+ theLoadedImage = SLoadedTexture::Load(
+ searchPath.toUtf8(), m_Context->GetFoundation(),
+ *m_InputStreamFactory, true,
+ m_Context->GetRenderContextType());
+ searchPath.prepend(QLatin1String("../"));
+ }
+ } else {
+ // Some textures, for example environment maps for custom materials,
+ // have absolute path at this point. It points to the wrong place with
+ // the new project structure, so we need to split it up and construct
+ // the new absolute path here.
+ QString wholePath = inImagePath.c_str();
+ QStringList splitPath = wholePath.split(QLatin1String("../"));
+ if (splitPath.size() > 1) {
+ QString searchPath = splitPath.at(0) + splitPath.at(1);
+ int loops = 0;
+ while (!theLoadedImage && ++loops <= 3) {
+ theLoadedImage = SLoadedTexture::Load(
+ searchPath.toUtf8(), m_Context->GetFoundation(),
+ *m_InputStreamFactory, true,
+ m_Context->GetRenderContextType());
+ searchPath = splitPath.at(0);
+ for (int i = 0; i < loops; i++)
+ searchPath.append(QLatin1String("../"));
+ searchPath.append(splitPath.at(1));
+ }
+ }
+ }
+ }
+ }
+
+ void enableReloadableResources(bool enable) override
+ {
+ m_reloadableResources = enable;
+ }
+
+ bool isReloadableResourcesEnabled() const override
+ {
+ return m_reloadableResources;
+ }
+
+ SImageTextureData LoadRenderImage(CRegisteredString inImagePath,
+ SLoadedTexture &inLoadedImage,
+ bool inForceScanForTransparency, bool inBsdfMipmaps) override
+ {
+ SStackPerfTimer __perfTimer(m_PerfTimer, "Image Upload");
+ {
+ Mutex::ScopedLock __mapLocker(m_LoadedImageSetMutex);
+ m_LoadedImageSet.insert(inImagePath);
+ }
+ pair<TImageMap::iterator, bool> theImage =
+ m_ImageMap.insert(make_pair(inImagePath, SImageEntry()));
+ bool wasInserted = theImage.second;
+ theImage.first->second.m_Loaded = true;
+ // inLoadedImage.EnsureMultiplerOfFour( m_Context->GetFoundation(), inImagePath.c_str() );
+
+ NVRenderTexture2D *theTexture = m_Context->CreateTexture2D();
+ if (inLoadedImage.data) {
+ qt3ds::render::NVRenderTextureFormats::Enum destFormat = inLoadedImage.format;
+ if (inBsdfMipmaps) {
+ if (m_Context->GetRenderContextType() == render::NVRenderContextValues::GLES2)
+ destFormat = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ else
+ destFormat = qt3ds::render::NVRenderTextureFormats::RGBA16F;
+ }
+ else {
+ theTexture->SetTextureData(
+ NVDataRef<QT3DSU8>((QT3DSU8 *)inLoadedImage.data, inLoadedImage.dataSizeInBytes), 0,
+ inLoadedImage.width, inLoadedImage.height, inLoadedImage.format, destFormat);
+ {
+ static int enable = qEnvironmentVariableIntValue("QT3DS_GENERATE_MIPMAPS");
+ if (enable) {
+ theTexture->SetMinFilter(NVRenderTextureMinifyingOp::LinearMipmapLinear);
+ theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear);
+ theTexture->GenerateMipmaps();
+ }
+ }
+ }
+
+ if (inBsdfMipmaps
+ && NVRenderTextureFormats::isUncompressedTextureFormat(inLoadedImage.format)) {
+ theTexture->SetMinFilter(NVRenderTextureMinifyingOp::LinearMipmapLinear);
+ Qt3DSRenderPrefilterTexture *theBSDFMipMap = theImage.first->second.m_BSDFMipMap;
+ if (theBSDFMipMap == NULL) {
+ theBSDFMipMap = Qt3DSRenderPrefilterTexture::Create(
+ m_Context, inLoadedImage.width, inLoadedImage.height, *theTexture,
+ destFormat, m_Context->GetFoundation());
+ theImage.first->second.m_BSDFMipMap = theBSDFMipMap;
+ }
+
+ if (theBSDFMipMap) {
+ theBSDFMipMap->Build(inLoadedImage.data, inLoadedImage.dataSizeInBytes,
+ inLoadedImage.format);
+ }
+ }
+ } else if (inLoadedImage.dds) {
+ theImage.first->second.m_Texture = theTexture;
+ bool supportsDXT = m_GPUSupportsDXT;
+ bool isDXT = NVRenderTextureFormats::isCompressedTextureFormat(inLoadedImage.format);
+ bool requiresDecompression = (supportsDXT == false && isDXT) || false;
+ // test code for DXT decompression
+ // if ( isDXT ) requiresDecompression = true;
+ if (requiresDecompression) {
+ qCWarning(WARNING, PERF_INFO,
+ "Image %s is DXT format which is unsupported by "
+ "the graphics subsystem, decompressing in CPU",
+ inImagePath.c_str());
+ }
+ STextureData theDecompressedImage;
+ for (int idx = 0; idx < inLoadedImage.dds->numMipmaps; ++idx) {
+ if (inLoadedImage.dds->mipwidth[idx] && inLoadedImage.dds->mipheight[idx]) {
+ if (requiresDecompression == false) {
+ theTexture->SetTextureData(
+ toU8DataRef((char *)inLoadedImage.dds->data[idx],
+ (QT3DSU32)inLoadedImage.dds->size[idx]),
+ (QT3DSU8)idx, (QT3DSU32)inLoadedImage.dds->mipwidth[idx],
+ (QT3DSU32)inLoadedImage.dds->mipheight[idx], inLoadedImage.format);
+ } else {
+ theDecompressedImage =
+ inLoadedImage.DecompressDXTImage(idx, &theDecompressedImage);
+
+ if (theDecompressedImage.data) {
+ theTexture->SetTextureData(
+ toU8DataRef((char *)theDecompressedImage.data,
+ (QT3DSU32)theDecompressedImage.dataSizeInBytes),
+ (QT3DSU8)idx, (QT3DSU32)inLoadedImage.dds->mipwidth[idx],
+ (QT3DSU32)inLoadedImage.dds->mipheight[idx],
+ theDecompressedImage.format);
+ }
+ }
+ }
+ }
+ if (theDecompressedImage.data)
+ inLoadedImage.ReleaseDecompressedTexture(theDecompressedImage);
+ }
+ if (wasInserted == true || inForceScanForTransparency)
+ theImage.first->second.m_TextureFlags.SetHasTransparency(
+ inLoadedImage.ScanForTransparency());
+ theImage.first->second.m_Texture = theTexture;
+ return theImage.first->second;
+ }
+
+ SImageTextureData LoadRenderImage(CRegisteredString inImagePath,
+ bool inForceScanForTransparency, bool inBsdfMipmaps) override
+ {
+ inImagePath = GetImagePath(inImagePath);
+
+ if (!inImagePath.IsValid())
+ return SImageEntry();
+
+ TImageMap::iterator theIter = m_ImageMap.find(inImagePath);
+ if (theIter == m_ImageMap.end() && inImagePath.IsValid()) {
+ NVScopedReleasable<SLoadedTexture> theLoadedImage;
+
+ doImageLoad(inImagePath, theLoadedImage);
+
+ if (theLoadedImage) {
+ return LoadRenderImage(inImagePath, *theLoadedImage, inForceScanForTransparency,
+ inBsdfMipmaps);
+ } else {
+ // We want to make sure that bad path fails once and doesn't fail over and over
+ // again
+ // which could slow down the system quite a bit.
+ pair<TImageMap::iterator, bool> theImage =
+ m_ImageMap.insert(make_pair(inImagePath, SImageEntry()));
+ theImage.first->second.m_Loaded = true;
+ qCWarning(WARNING, "Failed to load image: %s", inImagePath.c_str());
+ theIter = theImage.first;
+ }
+ }
+ return theIter->second;
+ }
+
+ qt3dsimp::SMultiLoadResult LoadPrimitive(const char8_t *inRelativePath)
+ {
+ CRegisteredString theName(m_StrTable->RegisterStr(inRelativePath));
+ if (m_PrimitiveNames[0].m_PrimitiveName.IsValid() == false) {
+ IStringTable &strTable(m_Context->GetStringTable());
+ m_PrimitiveNames[0].m_PrimitiveName = strTable.RegisterStr("#Rectangle");
+ m_PrimitiveNames[0].m_FileName = strTable.RegisterStr("Rectangle.mesh");
+ m_PrimitiveNames[1].m_PrimitiveName = strTable.RegisterStr("#Sphere");
+ m_PrimitiveNames[1].m_FileName = strTable.RegisterStr("Sphere.mesh");
+ m_PrimitiveNames[2].m_PrimitiveName = strTable.RegisterStr("#Cube");
+ m_PrimitiveNames[2].m_FileName = strTable.RegisterStr("Cube.mesh");
+ m_PrimitiveNames[3].m_PrimitiveName = strTable.RegisterStr("#Cone");
+ m_PrimitiveNames[3].m_FileName = strTable.RegisterStr("Cone.mesh");
+ m_PrimitiveNames[4].m_PrimitiveName = strTable.RegisterStr("#Cylinder");
+ m_PrimitiveNames[4].m_FileName = strTable.RegisterStr("Cylinder.mesh");
+ }
+ for (size_t idx = 0; idx < 5; ++idx) {
+ if (m_PrimitiveNames[idx].m_PrimitiveName == theName) {
+ CFileTools::CombineBaseAndRelative(GetPrimitivesDirectory(),
+ m_PrimitiveNames[idx].m_FileName, m_PathBuilder);
+ QT3DSU32 id = 1;
+ NVScopedRefCounted<IRefCountedInputStream> theInStream(
+ m_InputStreamFactory->GetStreamForFile(m_PathBuilder.c_str()));
+ if (theInStream)
+ return qt3dsimp::Mesh::LoadMulti(m_Context->GetAllocator(), *theInStream, id);
+ else {
+ qCCritical(INTERNAL_ERROR, "Unable to find mesh primitive %s",
+ m_PathBuilder.c_str());
+ return qt3dsimp::SMultiLoadResult();
+ }
+ }
+ }
+ return qt3dsimp::SMultiLoadResult();
+ }
+
+ virtual NVConstDataRef<QT3DSU8> CreatePackedPositionDataArray(
+ const qt3dsimp::SMultiLoadResult &inResult)
+ {
+ // we assume a position consists of 3 floats
+ QT3DSU32 vertexCount = inResult.m_Mesh->m_VertexBuffer.m_Data.size()
+ / inResult.m_Mesh->m_VertexBuffer.m_Stride;
+ QT3DSU32 dataSize = vertexCount * 3 * sizeof(QT3DSF32);
+ QT3DSF32 *posData = static_cast<QT3DSF32 *>(
+ QT3DS_ALLOC(m_Context->GetAllocator(), dataSize,
+ "SRenderMesh::CreatePackedPositionDataArray"));
+ QT3DSU8 *baseOffset = reinterpret_cast<QT3DSU8 *>(inResult.m_Mesh);
+ // copy position data
+ if (posData) {
+ QT3DSF32 *srcData = reinterpret_cast<QT3DSF32 *>(
+ inResult.m_Mesh->m_VertexBuffer.m_Data.begin(baseOffset));
+ QT3DSU32 srcStride = inResult.m_Mesh->m_VertexBuffer.m_Stride / sizeof(QT3DSF32);
+ QT3DSF32 *dstData = posData;
+ QT3DSU32 dstStride = 3;
+
+ for (QT3DSU32 i = 0; i < vertexCount; ++i) {
+ dstData[0] = srcData[0];
+ dstData[1] = srcData[1];
+ dstData[2] = srcData[2];
+
+ dstData += dstStride;
+ srcData += srcStride;
+ }
+
+ return toConstDataRef(reinterpret_cast<const qt3ds::QT3DSU8 *>(posData), dataSize);
+ }
+
+ return NVConstDataRef<QT3DSU8>();
+ }
+
+ SRenderMesh *createRenderMesh(const qt3dsimp::SMultiLoadResult &result)
+ {
+ SRenderMesh *theNewMesh = QT3DS_NEW(m_Context->GetAllocator(), SRenderMesh)(
+ qt3ds::render::NVRenderDrawMode::Triangles,
+ qt3ds::render::NVRenderWinding::CounterClockwise, result.m_Id,
+ m_Context->GetAllocator());
+ QT3DSU8 *baseAddress = reinterpret_cast<QT3DSU8 *>(result.m_Mesh);
+ NVConstDataRef<QT3DSU8> theVBufData(
+ result.m_Mesh->m_VertexBuffer.m_Data.begin(baseAddress),
+ result.m_Mesh->m_VertexBuffer.m_Data.size());
+
+ NVRenderVertexBuffer *theVertexBuffer = m_Context->CreateVertexBuffer(
+ qt3ds::render::NVRenderBufferUsageType::Static,
+ result.m_Mesh->m_VertexBuffer.m_Data.m_Size,
+ result.m_Mesh->m_VertexBuffer.m_Stride, theVBufData);
+
+ // create a tight packed position data VBO
+ // this should improve our depth pre pass rendering
+ NVRenderVertexBuffer *thePosVertexBuffer = nullptr;
+ NVConstDataRef<QT3DSU8> posData = CreatePackedPositionDataArray(result);
+ if (posData.size()) {
+ thePosVertexBuffer
+ = m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Static,
+ posData.size(), 3 * sizeof(QT3DSF32), posData);
+ }
+
+ NVRenderIndexBuffer *theIndexBuffer = nullptr;
+ if (result.m_Mesh->m_IndexBuffer.m_Data.size()) {
+ using qt3ds::render::NVRenderComponentTypes;
+ QT3DSU32 theIndexBufferSize = result.m_Mesh->m_IndexBuffer.m_Data.size();
+ NVRenderComponentTypes::Enum bufComponentType =
+ result.m_Mesh->m_IndexBuffer.m_ComponentType;
+ QT3DSU32 sizeofType
+ = qt3ds::render::NVRenderComponentTypes::getSizeofType(bufComponentType);
+
+ if (sizeofType == 2 || sizeofType == 4) {
+ // Ensure type is unsigned; else things will fail in rendering pipeline.
+ if (bufComponentType == NVRenderComponentTypes::QT3DSI16)
+ bufComponentType = NVRenderComponentTypes::QT3DSU16;
+ if (bufComponentType == NVRenderComponentTypes::QT3DSI32)
+ bufComponentType = NVRenderComponentTypes::QT3DSU32;
+
+ NVConstDataRef<QT3DSU8> theIBufData(
+ result.m_Mesh->m_IndexBuffer.m_Data.begin(baseAddress),
+ result.m_Mesh->m_IndexBuffer.m_Data.size());
+ theIndexBuffer = m_Context->CreateIndexBuffer(
+ qt3ds::render::NVRenderBufferUsageType::Static, bufComponentType,
+ theIndexBufferSize, theIBufData);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ nvvector<qt3ds::render::NVRenderVertexBufferEntry> &theEntryBuffer(m_EntryBuffer);
+ theEntryBuffer.resize(result.m_Mesh->m_VertexBuffer.m_Entries.size());
+ for (QT3DSU32 entryIdx = 0,
+ entryEnd = result.m_Mesh->m_VertexBuffer.m_Entries.size();
+ entryIdx < entryEnd; ++entryIdx) {
+ theEntryBuffer[entryIdx]
+ = result.m_Mesh->m_VertexBuffer.m_Entries.index(baseAddress, entryIdx)
+ .ToVertexBufferEntry(baseAddress);
+ }
+ // create our attribute layout
+ NVRenderAttribLayout *theAttribLayout
+ = m_Context->CreateAttributeLayout(theEntryBuffer);
+ // create our attribute layout for depth pass
+ qt3ds::render::NVRenderVertexBufferEntry theEntries[] = {
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3),
+ };
+ NVRenderAttribLayout *theAttribLayoutDepth
+ = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 1));
+
+ // create input assembler object
+ QT3DSU32 strides = result.m_Mesh->m_VertexBuffer.m_Stride;
+ QT3DSU32 offsets = 0;
+ NVRenderInputAssembler *theInputAssembler = m_Context->CreateInputAssembler(
+ theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer,
+ toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1),
+ result.m_Mesh->m_DrawMode);
+
+ // create depth input assembler object
+ QT3DSU32 posStrides = thePosVertexBuffer ? 3 * sizeof(QT3DSF32) : strides;
+ NVRenderInputAssembler *theInputAssemblerDepth = m_Context->CreateInputAssembler(
+ theAttribLayoutDepth,
+ toConstDataRef(thePosVertexBuffer ? &thePosVertexBuffer : &theVertexBuffer, 1),
+ theIndexBuffer, toConstDataRef(&posStrides, 1), toConstDataRef(&offsets, 1),
+ result.m_Mesh->m_DrawMode);
+
+ NVRenderInputAssembler *theInputAssemblerPoints = m_Context->CreateInputAssembler(
+ theAttribLayoutDepth,
+ toConstDataRef(thePosVertexBuffer ? &thePosVertexBuffer : &theVertexBuffer, 1),
+ nullptr, toConstDataRef(&posStrides, 1), toConstDataRef(&offsets, 1),
+ NVRenderDrawMode::Points);
+
+ if (!theInputAssembler || !theInputAssemblerDepth || !theInputAssemblerPoints) {
+ QT3DS_ASSERT(false);
+ return nullptr;
+ }
+ theNewMesh->m_Joints.resize(result.m_Mesh->m_Joints.size());
+ for (QT3DSU32 jointIdx = 0, jointEnd = result.m_Mesh->m_Joints.size();
+ jointIdx < jointEnd; ++jointIdx) {
+ const qt3dsimp::Joint &theImportJoint(
+ result.m_Mesh->m_Joints.index(baseAddress, jointIdx));
+ SRenderJoint &theNewJoint(theNewMesh->m_Joints[jointIdx]);
+ theNewJoint.m_JointID = theImportJoint.m_JointID;
+ theNewJoint.m_ParentID = theImportJoint.m_ParentID;
+ memCopy(theNewJoint.m_invBindPose, theImportJoint.m_invBindPose,
+ 16 * sizeof(QT3DSF32));
+ memCopy(theNewJoint.m_localToGlobalBoneSpace,
+ theImportJoint.m_localToGlobalBoneSpace, 16 * sizeof(QT3DSF32));
+ }
+
+ for (QT3DSU32 subsetIdx = 0, subsetEnd = result.m_Mesh->m_Subsets.size();
+ subsetIdx < subsetEnd; ++subsetIdx) {
+ SRenderSubset theSubset(m_Context->GetAllocator());
+ const qt3dsimp::MeshSubset &source(
+ result.m_Mesh->m_Subsets.index(baseAddress, subsetIdx));
+ theSubset.m_Bounds = source.m_Bounds;
+ theSubset.m_Count = source.m_Count;
+ theSubset.m_Offset = source.m_Offset;
+ theSubset.m_Joints = theNewMesh->m_Joints;
+ theSubset.m_Name = m_StrTable->RegisterStr(source.m_Name.begin(baseAddress));
+ theVertexBuffer->addRef();
+ theSubset.m_VertexBuffer = theVertexBuffer;
+ if (thePosVertexBuffer) {
+ thePosVertexBuffer->addRef();
+ theSubset.m_PosVertexBuffer = thePosVertexBuffer;
+ }
+ if (theIndexBuffer) {
+ theIndexBuffer->addRef();
+ theSubset.m_IndexBuffer = theIndexBuffer;
+ }
+ theSubset.m_InputAssembler = theInputAssembler;
+ theSubset.m_InputAssemblerDepth = theInputAssemblerDepth;
+ theSubset.m_InputAssemblerPoints = theInputAssemblerPoints;
+ theSubset.m_PrimitiveType = result.m_Mesh->m_DrawMode;
+ theInputAssembler->addRef();
+ theInputAssemblerDepth->addRef();
+ theSubset.m_InputAssemblerPoints->addRef();
+ theNewMesh->m_Subsets.push_back(theSubset);
+ }
+ // If we want to, we can break up models into sub-subsets.
+ // These are assumed to use the same material as the outer subset but have fewer triangles
+ // and should have a more exact bounding box. This sort of thing helps with using the
+ // frustum culling system but it is really done incorrectly.
+ // It should be done via some sort of oct-tree mechanism and so that the sub-subsets
+ // are spatially sorted and it should only be done upon save-to-binary with the
+ // results saved out to disk. As you can see, doing it properly requires some real
+ // engineering effort so it is somewhat unlikely it will ever happen.
+ // Or it could be done on import if someone really wants to change the mesh buffer
+ // format. Either way it isn't going to happen here and it isn't going to happen this way
+ // but this is a working example of using the technique.
+#ifdef QT3DS_RENDER_GENERATE_SUB_SUBSETS
+ Option<qt3ds::render::NVRenderVertexBufferEntry> thePosAttrOpt
+ = theVertexBuffer->GetEntryByName("attr_pos");
+ bool hasPosAttr = thePosAttrOpt.hasValue()
+ && thePosAttrOpt->m_ComponentType == qt3ds::render::NVRenderComponentTypes::QT3DSF32
+ && thePosAttrOpt->m_NumComponents == 3;
+
+ for (size_t subsetIdx = 0, subsetEnd = theNewMesh->m_Subsets.size();
+ subsetIdx < subsetEnd; ++subsetIdx) {
+ SRenderSubset &theOuterSubset = theNewMesh->m_Subsets[subsetIdx];
+ if (theOuterSubset.m_Count && theIndexBuffer
+ && theIndexBuffer->GetComponentType()
+ == qt3ds::render::NVRenderComponentTypes::QT3DSU16
+ && theNewMesh->m_DrawMode == NVRenderDrawMode::Triangles && hasPosAttr) {
+ // Num tris in a sub subset.
+ QT3DSU32 theSubsetSize = 3334 * 3; // divisible by three.
+ size_t theNumSubSubsets = ((theOuterSubset.m_Count - 1) / theSubsetSize) + 1;
+ QT3DSU32 thePosAttrOffset = thePosAttrOpt->m_FirstItemOffset;
+ const QT3DSU8 *theVertData = result.m_Mesh->m_VertexBuffer.m_Data.begin();
+ const QT3DSU8 *theIdxData = result.m_Mesh->m_IndexBuffer.m_Data.begin();
+ QT3DSU32 theVertStride = result.m_Mesh->m_VertexBuffer.m_Stride;
+ QT3DSU32 theOffset = theOuterSubset.m_Offset;
+ QT3DSU32 theCount = theOuterSubset.m_Count;
+ for (size_t subSubsetIdx = 0, subSubsetEnd = theNumSubSubsets;
+ subSubsetIdx < subSubsetEnd; ++subSubsetIdx) {
+ SRenderSubsetBase theBase;
+ theBase.m_Offset = theOffset;
+ theBase.m_Count = NVMin(theSubsetSize, theCount);
+ theBase.m_Bounds.setEmpty();
+ theCount -= theBase.m_Count;
+ theOffset += theBase.m_Count;
+ // Create new bounds.
+ // Offset is in item size, not bytes.
+ const QT3DSU16 *theSubsetIdxData
+ = reinterpret_cast<const QT3DSU16 *>(theIdxData + theBase.m_Offset * 2);
+ for (size_t theIdxIdx = 0, theIdxEnd = theBase.m_Count;
+ theIdxIdx < theIdxEnd; ++theIdxIdx) {
+ QT3DSU32 theVertOffset = theSubsetIdxData[theIdxIdx] * theVertStride;
+ theVertOffset += thePosAttrOffset;
+ QT3DSVec3 thePos = *(
+ reinterpret_cast<const QT3DSVec3 *>(theVertData + theVertOffset));
+ theBase.m_Bounds.include(thePos);
+ }
+ theOuterSubset.m_SubSubsets.push_back(theBase);
+ }
+ } else {
+ SRenderSubsetBase theBase;
+ theBase.m_Bounds = theOuterSubset.m_Bounds;
+ theBase.m_Count = theOuterSubset.m_Count;
+ theBase.m_Offset = theOuterSubset.m_Offset;
+ theOuterSubset.m_SubSubsets.push_back(theBase);
+ }
+ }
+#endif
+ if (posData.size()) {
+ m_Context->GetAllocator().deallocate(
+ static_cast<void *>(const_cast<qt3ds::QT3DSU8 *>(posData.begin())));
+ }
+
+ return theNewMesh;
+ }
+
+ void loadCustomMesh(const QString &name, qt3dsimp::Mesh *mesh) override
+ {
+ if (!name.isEmpty() && mesh) {
+ CRegisteredString meshName = m_StrTable->RegisterStr(name);
+ pair<TMeshMap::iterator, bool> theMesh
+ = m_MeshMap.insert({ meshName, static_cast<SRenderMesh *>(nullptr) });
+ // Only create the mesh if it doesn't yet exist
+ if (theMesh.second) {
+ qt3dsimp::SMultiLoadResult result;
+ result.m_Mesh = mesh;
+ theMesh.first->second = createRenderMesh(result);
+ }
+ }
+ }
+
+ SRenderMesh *LoadMesh(CRegisteredString inMeshPath) override
+ {
+ if (inMeshPath.IsValid() == false)
+ return nullptr;
+ pair<TMeshMap::iterator, bool> theMesh =
+ m_MeshMap.insert(make_pair(inMeshPath, static_cast<SRenderMesh *>(nullptr)));
+ if (theMesh.second) {
+ // Check to see if this is primitive
+ qt3dsimp::SMultiLoadResult theResult = LoadPrimitive(inMeshPath);
+
+ // Attempt a load from the filesystem if this mesh isn't a primitive.
+ if (!theResult.m_Mesh) {
+ m_PathBuilder = inMeshPath;
+ TStr::size_type pound = m_PathBuilder.rfind('#');
+ QT3DSU32 id = 0;
+ if (pound != TStr::npos) {
+ id = QT3DSU32(atoi(m_PathBuilder.c_str() + pound + 1));
+ m_PathBuilder.erase(m_PathBuilder.begin() + pound, m_PathBuilder.end());
+ }
+ NVScopedRefCounted<IRefCountedInputStream> theStream(
+ m_InputStreamFactory->GetStreamForFile(m_PathBuilder.c_str()));
+ if (theStream) {
+ theResult = qt3dsimp::Mesh::LoadMulti(
+ m_Context->GetAllocator(), *theStream, id);
+ }
+ if (!theResult.m_Mesh)
+ qCWarning(WARNING, "Failed to load mesh: %s", m_PathBuilder.c_str());
+ }
+
+ if (theResult.m_Mesh) {
+ theMesh.first->second = createRenderMesh(theResult);
+ m_Context->GetAllocator().deallocate(theResult.m_Mesh);
+ }
+ }
+ return theMesh.first->second;
+ }
+
+ SRenderMesh *CreateMesh(Qt3DSBCharPtr inSourcePath, QT3DSU8 *inVertData, QT3DSU32 inNumVerts,
+ QT3DSU32 inVertStride, QT3DSU32 *inIndexData, QT3DSU32 inIndexCount,
+ qt3ds::NVBounds3 inBounds) override
+ {
+ CRegisteredString sourcePath = m_StrTable->RegisterStr(inSourcePath);
+
+ // eastl::pair<CRegisteredString, SRenderMesh*> thePair(sourcePath, (SRenderMesh*)NULL);
+ pair<TMeshMap::iterator, bool> theMesh;
+ // Make sure there isn't already a buffer entry for this mesh.
+ if (m_MeshMap.contains(sourcePath)) {
+ theMesh = make_pair<TMeshMap::iterator, bool>(m_MeshMap.find(sourcePath), true);
+ } else {
+ theMesh = m_MeshMap.insert(make_pair(sourcePath, (SRenderMesh *)NULL));
+ }
+
+ if (theMesh.second == true) {
+ SRenderMesh *theNewMesh = QT3DS_NEW(m_Context->GetAllocator(), SRenderMesh)(
+ qt3ds::render::NVRenderDrawMode::Triangles,
+ qt3ds::render::NVRenderWinding::CounterClockwise, 0, m_Context->GetAllocator());
+
+ // If we failed to create the RenderMesh, return a failure.
+ if (!theNewMesh) {
+ QT3DS_ASSERT(false);
+ return NULL;
+ }
+
+ // Get rid of any old mesh that was sitting here and fill it with a new one.
+ // NOTE : This is assuming that the source of our mesh data doesn't do its own memory
+ // management and always returns new buffer pointers every time.
+ // Don't know for sure if that's what we'll get from our intended sources, but that's
+ // easily
+ // adjustable by looking for matching pointers in the Subsets.
+ if (theNewMesh && theMesh.first->second != NULL) {
+ delete theMesh.first->second;
+ theMesh.first->second = NULL;
+ }
+
+ theMesh.first->second = theNewMesh;
+ QT3DSU32 vertDataSize = inNumVerts * inVertStride;
+ NVConstDataRef<QT3DSU8> theVBufData(inVertData, vertDataSize);
+ // NVConstDataRef<QT3DSU8> theVBufData( theResult.m_Mesh->m_VertexBuffer.m_Data.begin(
+ // baseAddress )
+ // , theResult.m_Mesh->m_VertexBuffer.m_Data.size() );
+
+ NVRenderVertexBuffer *theVertexBuffer =
+ m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Static,
+ vertDataSize, inVertStride, theVBufData);
+ NVRenderIndexBuffer *theIndexBuffer = NULL;
+ if (inIndexData != NULL && inIndexCount > 3) {
+ NVConstDataRef<QT3DSU8> theIBufData((QT3DSU8 *)inIndexData, inIndexCount * sizeof(QT3DSU32));
+ theIndexBuffer =
+ m_Context->CreateIndexBuffer(qt3ds::render::NVRenderBufferUsageType::Static,
+ qt3ds::render::NVRenderComponentTypes::QT3DSU32,
+ inIndexCount * sizeof(QT3DSU32), theIBufData);
+ }
+
+ // WARNING
+ // Making an assumption here about the contents of the stream
+ // PKC TODO : We may have to consider some other format.
+ qt3ds::render::NVRenderVertexBufferEntry theEntries[] = {
+ qt3ds::render::NVRenderVertexBufferEntry("attr_pos",
+ qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3),
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_uv", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12),
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_norm", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3, 18),
+ };
+
+ // create our attribute layout
+ NVRenderAttribLayout *theAttribLayout =
+ m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 3));
+ /*
+ // create our attribute layout for depth pass
+ qt3ds::render::NVRenderVertexBufferEntry theEntriesDepth[] = {
+ qt3ds::render::NVRenderVertexBufferEntry( "attr_pos",
+ qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3 ),
+ };
+ NVRenderAttribLayout* theAttribLayoutDepth = m_Context->CreateAttributeLayout(
+ toConstDataRef( theEntriesDepth, 1 ) );
+ */
+ // create input assembler object
+ QT3DSU32 strides = inVertStride;
+ QT3DSU32 offsets = 0;
+ NVRenderInputAssembler *theInputAssembler = m_Context->CreateInputAssembler(
+ theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer,
+ toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1),
+ qt3ds::render::NVRenderDrawMode::Triangles);
+
+ if (!theInputAssembler) {
+ QT3DS_ASSERT(false);
+ return NULL;
+ }
+
+ // Pull out just the mesh object name from the total path
+ eastl::string fullName(inSourcePath);
+ eastl::string subName(inSourcePath);
+ if (fullName.rfind("#") != eastl::string::npos) {
+ subName = fullName.substr(fullName.rfind("#"), eastl::string::npos);
+ }
+
+ theNewMesh->m_Joints.clear();
+ SRenderSubset theSubset(m_Context->GetAllocator());
+ theSubset.m_Bounds = inBounds;
+ theSubset.m_Count = inIndexCount;
+ theSubset.m_Offset = 0;
+ theSubset.m_Joints = theNewMesh->m_Joints;
+ theSubset.m_Name = m_StrTable->RegisterStr(subName.c_str());
+ theVertexBuffer->addRef();
+ theSubset.m_VertexBuffer = theVertexBuffer;
+ theSubset.m_PosVertexBuffer = NULL;
+ if (theIndexBuffer)
+ theIndexBuffer->addRef();
+ theSubset.m_IndexBuffer = theIndexBuffer;
+ theSubset.m_InputAssembler = theInputAssembler;
+ theSubset.m_InputAssemblerDepth = theInputAssembler;
+ theSubset.m_InputAssemblerPoints = theInputAssembler;
+ theSubset.m_PrimitiveType = qt3ds::render::NVRenderDrawMode::Triangles;
+ theSubset.m_InputAssembler->addRef();
+ theSubset.m_InputAssemblerDepth->addRef();
+ theSubset.m_InputAssemblerPoints->addRef();
+ theNewMesh->m_Subsets.push_back(theSubset);
+ }
+
+ return theMesh.first->second;
+ }
+
+ void ReleaseMesh(SRenderMesh &inMesh)
+ {
+ for (QT3DSU32 subsetIdx = 0, subsetEnd = inMesh.m_Subsets.size(); subsetIdx < subsetEnd;
+ ++subsetIdx) {
+ inMesh.m_Subsets[subsetIdx].m_VertexBuffer->release();
+ if (inMesh.m_Subsets[subsetIdx].m_PosVertexBuffer) // can be NULL
+ inMesh.m_Subsets[subsetIdx].m_PosVertexBuffer->release();
+ if (inMesh.m_Subsets[subsetIdx].m_IndexBuffer) // can be NULL
+ inMesh.m_Subsets[subsetIdx].m_IndexBuffer->release();
+ inMesh.m_Subsets[subsetIdx].m_InputAssembler->release();
+ inMesh.m_Subsets[subsetIdx].m_InputAssemblerDepth->release();
+ if (inMesh.m_Subsets[subsetIdx].m_InputAssemblerPoints)
+ inMesh.m_Subsets[subsetIdx].m_InputAssemblerPoints->release();
+ }
+ NVDelete(m_Context->GetAllocator(), &inMesh);
+ }
+ void ReleaseTexture(SImageEntry &inEntry)
+ {
+ if (inEntry.m_Texture)
+ inEntry.m_Texture->release();
+ if (inEntry.m_BSDFMipMap)
+ inEntry.m_BSDFMipMap->release();
+ }
+ void Clear() override
+ {
+ m_reloadableTextures.clear();
+ for (TMeshMap::iterator iter = m_MeshMap.begin(), end = m_MeshMap.end(); iter != end;
+ ++iter) {
+ SRenderMesh *theMesh = iter->second;
+ if (theMesh)
+ ReleaseMesh(*theMesh);
+ }
+ m_MeshMap.clear();
+ for (TImageMap::iterator iter = m_ImageMap.begin(), end = m_ImageMap.end(); iter != end;
+ ++iter) {
+ SImageEntry &theEntry = iter->second;
+ ReleaseTexture(theEntry);
+ }
+ m_ImageMap.clear();
+ m_AliasImageMap.clear();
+ {
+ Mutex::ScopedLock __locker(m_LoadedImageSetMutex);
+ m_LoadedImageSet.clear();
+ }
+ }
+ void InvalidateBuffer(CRegisteredString inSourcePath) override
+ {
+ {
+ TMeshMap::iterator iter = m_MeshMap.find(inSourcePath);
+ if (iter != m_MeshMap.end()) {
+ if (iter->second)
+ ReleaseMesh(*iter->second);
+ m_MeshMap.erase(iter);
+ return;
+ }
+ }
+ {
+ TImageMap::iterator iter = m_ImageMap.find(inSourcePath);
+ if (iter != m_ImageMap.end()) {
+ SImageEntry &theEntry = iter->second;
+ ReleaseTexture(theEntry);
+ m_ImageMap.erase(inSourcePath);
+ {
+ Mutex::ScopedLock __locker(m_LoadedImageSetMutex);
+ m_LoadedImageSet.erase(inSourcePath);
+ }
+ }
+ }
+ }
+ IStringTable &GetStringTable() override { return *m_StrTable; }
+};
+}
+
+IBufferManager &IBufferManager::Create(NVRenderContext &inRenderContext, IStringTable &inStrTable,
+ IInputStreamFactory &inFactory, IPerfTimer &inPerfTimer)
+{
+ return *QT3DS_NEW(inRenderContext.GetAllocator(), SBufferManager)(inRenderContext, inStrTable,
+ inFactory, inPerfTimer);
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h
new file mode 100644
index 0000000..070ab67
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_RENDER_BUFFER_MANAGER_H
+#define QT3DS_RENDER_BUFFER_MANAGER_H
+#include "Qt3DSRender.h"
+#include "EASTL/utility.h" //pair
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/StringTable.h"
+#include "Qt3DSRenderImageTextureData.h"
+#include "foundation/Qt3DSBounds3.h"
+
+namespace qt3dsimp {
+ struct Mesh;
+}
+
+namespace qt3ds {
+namespace render {
+
+ class IBufferManager : public NVRefCounted
+ {
+ protected:
+ virtual ~IBufferManager() {}
+
+ public:
+ // Path manipulation used to get the final path form a base path plus relative extension
+ virtual CRegisteredString CombineBaseAndRelative(const char8_t *inBase,
+ const char8_t *inRelative) = 0;
+ virtual void SetImageHasTransparency(CRegisteredString inSourcePath,
+ bool inHasTransparency) = 0;
+ virtual bool GetImageHasTransparency(CRegisteredString inSourcePath) const = 0;
+ virtual void SetImageTransparencyToFalseIfNotSet(CRegisteredString inSourcePath) = 0;
+ virtual void SetInvertImageUVCoords(CRegisteredString inSourcePath,
+ bool inShouldInvertCoords) = 0;
+
+ // Returns true if this image has been loaded into memory
+ // This call is threadsafe. Nothing else on this object is guaranteed to be.
+ virtual bool IsImageLoaded(CRegisteredString inSourcePath) = 0;
+
+ // Alias one image path with another image path. Optionally this object will ignore the
+ // call if
+ // the source path is already loaded. Aliasing is currently used to allow a default image
+ // to be shown
+ // in place of an image that is loading offline.
+ // Returns true if the image was aliased, false otherwise.
+ virtual bool AliasImagePath(CRegisteredString inSourcePath, CRegisteredString inAliasPath,
+ bool inIgnoreIfLoaded) = 0;
+ virtual void UnaliasImagePath(CRegisteredString inSourcePath) = 0;
+
+ // Returns the given source path unless the source path is aliased; in which case returns
+ // the aliased path.
+ virtual CRegisteredString GetImagePath(CRegisteredString inSourcePath) = 0;
+ // Returns a texture and a boolean indicating if this texture has transparency in it or not.
+ // Can't name this LoadImage because that gets mangled by windows to LoadImageA (uggh)
+ // In some cases we need to only scan particular images for transparency.
+ virtual SImageTextureData LoadRenderImage(CRegisteredString inImagePath,
+ SLoadedTexture &inTexture,
+ bool inForceScanForTransparency = false,
+ bool inBsdfMipmaps = false) = 0;
+ virtual SImageTextureData LoadRenderImage(CRegisteredString inSourcePath,
+ bool inForceScanForTransparency = false,
+ bool inBsdfMipmaps = false) = 0;
+
+ virtual ReloadableTexturePtr CreateReloadableImage(CRegisteredString inSourcePath,
+ bool inForceScanForTransparency = false,
+ bool inBsdfMipmaps = false) = 0;
+ virtual void enableReloadableResources(bool enable) = 0;
+ virtual bool isReloadableResourcesEnabled() const = 0;
+
+ virtual void loadSet(const QSet<QString> &imageSet) = 0;
+ virtual void unloadSet(const QSet<QString> &imageSet) = 0;
+
+ virtual void loadCustomMesh(const QString &name, qt3dsimp::Mesh *mesh) = 0;
+ virtual SRenderMesh *LoadMesh(CRegisteredString inSourcePath) = 0;
+
+ virtual SRenderMesh *CreateMesh(const char *inSourcePath, QT3DSU8 *inVertData,
+ QT3DSU32 inNumVerts, QT3DSU32 inVertStride, QT3DSU32 *inIndexData,
+ QT3DSU32 inIndexCount, qt3ds::NVBounds3 inBounds) = 0;
+
+ // Remove *all* buffers from the buffer manager;
+ virtual void Clear() = 0;
+ virtual void InvalidateBuffer(CRegisteredString inSourcePath) = 0;
+ virtual IStringTable &GetStringTable() = 0;
+
+ static IBufferManager &Create(NVRenderContext &inRenderContext, IStringTable &inStrTable,
+ IInputStreamFactory &inInputStreamFactory,
+ IPerfTimer &inTimer);
+ };
+}
+}
+
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp
new file mode 100644
index 0000000..72495cd
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp
@@ -0,0 +1,538 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderImageBatchLoader.h"
+#include "foundation/Qt3DSMutex.h"
+#include "foundation/Qt3DSSync.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/StringTable.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "Qt3DSRenderBufferManager.h"
+#include "Qt3DSRenderThreadPool.h"
+#include "Qt3DSRenderImageScaler.h"
+#include "Qt3DSRenderLoadedTexture.h"
+#include "foundation/Qt3DSInvasiveLinkedList.h"
+#include "foundation/Qt3DSPool.h"
+#include "foundation/Qt3DSPerfTimer.h"
+
+using namespace qt3ds::render;
+
+namespace {
+
+struct SImageLoaderBatch;
+typedef Mutex::ScopedLock TScopedLock;
+
+struct SLoadingImage
+{
+ SImageLoaderBatch *m_Batch;
+ CRegisteredString m_SourcePath;
+ QT3DSU64 m_TaskId;
+ SLoadingImage *m_Tail;
+
+ // Called from main thread
+ SLoadingImage(CRegisteredString inSourcePath)
+ : m_Batch(NULL)
+ , m_SourcePath(inSourcePath)
+ , m_TaskId(0)
+ , m_Tail(NULL)
+ {
+ }
+ SLoadingImage()
+ : m_Batch(NULL)
+ , m_TaskId(0)
+ , m_Tail(NULL)
+ {
+ }
+ // Called from main thread
+ void Setup(SImageLoaderBatch &inBatch);
+
+ // Called from loader thread
+ static void LoadImage(void *inImg);
+
+ // Potentially called from loader thread
+ static void TaskCancelled(void *inImg);
+};
+
+struct SLoadingImageTailOp
+{
+ SLoadingImage *get(SLoadingImage &inImg) { return inImg.m_Tail; }
+ void set(SLoadingImage &inImg, SLoadingImage *inItem) { inImg.m_Tail = inItem; }
+};
+
+typedef InvasiveSingleLinkedList<SLoadingImage, SLoadingImageTailOp> TLoadingImageList;
+
+struct SBatchLoader;
+
+struct SImageLoaderBatch
+{
+ // All variables setup in main thread and constant from then on except
+ // loaded image count.
+ SBatchLoader &m_Loader;
+ NVScopedRefCounted<IImageLoadListener> m_LoadListener;
+ Sync m_LoadEvent;
+ Mutex m_LoadMutex;
+ TLoadingImageList m_Images;
+
+ TImageBatchId m_BatchId;
+ // Incremented in main thread
+ QT3DSU32 m_LoadedOrCanceledImageCount;
+ QT3DSU32 m_FinalizedImageCount;
+ QT3DSU32 m_NumImages;
+ NVRenderContextType m_contextType;
+ bool m_preferKTX;
+ bool m_ibl;
+
+ // Called from main thread
+ static SImageLoaderBatch *CreateLoaderBatch(SBatchLoader &inLoader, TImageBatchId inBatchId,
+ NVConstDataRef<CRegisteredString> inSourcePaths,
+ CRegisteredString inImageTillLoaded,
+ IImageLoadListener *inListener,
+ NVRenderContextType contextType,
+ bool preferKTX, bool ibl);
+
+ // Called from main thread
+ SImageLoaderBatch(SBatchLoader &inLoader, IImageLoadListener *inLoadListener,
+ const TLoadingImageList &inImageList, TImageBatchId inBatchId,
+ QT3DSU32 inImageCount, NVRenderContextType contextType,
+ bool preferKTX, bool ibl);
+
+ // Called from main thread
+ ~SImageLoaderBatch();
+
+ // Called from main thread
+ bool IsLoadingFinished()
+ {
+ Mutex::ScopedLock __locker(m_LoadMutex);
+ return m_LoadedOrCanceledImageCount >= m_NumImages;
+ }
+
+ bool IsFinalizedFinished()
+ {
+ Mutex::ScopedLock __locker(m_LoadMutex);
+ return m_FinalizedImageCount >= m_NumImages;
+ }
+
+ void IncrementLoadedImageCount()
+ {
+ Mutex::ScopedLock __locker(m_LoadMutex);
+ ++m_LoadedOrCanceledImageCount;
+ }
+ void IncrementFinalizedImageCount()
+ {
+ Mutex::ScopedLock __locker(m_LoadMutex);
+ ++m_FinalizedImageCount;
+ }
+ // Called from main thread
+ void Cancel();
+ void Cancel(CRegisteredString inSourcePath);
+};
+
+struct SBatchLoadedImage
+{
+ CRegisteredString m_SourcePath;
+ SLoadedTexture *m_Texture;
+ SImageLoaderBatch *m_Batch;
+ SBatchLoadedImage()
+ : m_Texture(NULL)
+ , m_Batch(NULL)
+ {
+ }
+
+ // Called from loading thread
+ SBatchLoadedImage(CRegisteredString inSourcePath, SLoadedTexture *inTexture,
+ SImageLoaderBatch &inBatch)
+ : m_SourcePath(inSourcePath)
+ , m_Texture(inTexture)
+ , m_Batch(&inBatch)
+ {
+ }
+
+ // Called from main thread
+ bool Finalize(IBufferManager &inMgr);
+};
+
+struct SBatchLoader : public IImageBatchLoader
+{
+ typedef nvhash_map<TImageBatchId, SImageLoaderBatch *> TImageLoaderBatchMap;
+ typedef nvhash_map<CRegisteredString, TImageBatchId> TSourcePathToBatchMap;
+ typedef Pool<SLoadingImage, ForwardingAllocator> TLoadingImagePool;
+ typedef Pool<SImageLoaderBatch, ForwardingAllocator> TBatchPool;
+
+ // Accessed from loader thread
+ NVFoundationBase &m_Foundation;
+ volatile QT3DSI32 mRefCount;
+ // Accessed from loader thread
+ IInputStreamFactory &m_InputStreamFactory;
+ //!!Not threadsafe! accessed only from main thread
+ IBufferManager &m_BufferManager;
+ // Accessed from main thread
+ IThreadPool &m_ThreadPool;
+ // Accessed from both threads
+ IPerfTimer &m_PerfTimer;
+ // main thread
+ TImageBatchId m_NextBatchId;
+ // main thread
+ TImageLoaderBatchMap m_Batches;
+ // main thread
+ Mutex m_LoaderMutex;
+
+ // Both loader and main threads
+ nvvector<SBatchLoadedImage> m_LoadedImages;
+ // main thread
+ nvvector<TImageBatchId> m_FinishedBatches;
+ // main thread
+ TSourcePathToBatchMap m_SourcePathToBatches;
+ // main thread
+ nvvector<SLoadingImage> m_LoaderBuilderWorkspace;
+ TLoadingImagePool m_LoadingImagePool;
+ TBatchPool m_BatchPool;
+
+ SBatchLoader(NVFoundationBase &inFoundation, IInputStreamFactory &inFactory,
+ IBufferManager &inBufferManager, IThreadPool &inThreadPool, IPerfTimer &inTimer)
+ : m_Foundation(inFoundation)
+ , mRefCount(0)
+ , m_InputStreamFactory(inFactory)
+ , m_BufferManager(inBufferManager)
+ , m_ThreadPool(inThreadPool)
+ , m_PerfTimer(inTimer)
+ , m_NextBatchId(1)
+ , m_Batches(inFoundation.getAllocator(), "SBatchLoader::m_Batches")
+ , m_LoaderMutex(inFoundation.getAllocator())
+ , m_LoadedImages(inFoundation.getAllocator(), "SBatchLoader::m_LoadedImages")
+ , m_FinishedBatches(inFoundation.getAllocator(), "SBatchLoader::m_FinishedBatches")
+ , m_SourcePathToBatches(inFoundation.getAllocator(), "SBatchLoader::m_SourcePathToBatches")
+ , m_LoaderBuilderWorkspace(inFoundation.getAllocator(),
+ "SBatchLoader::m_LoaderBuilderWorkspace")
+ , m_LoadingImagePool(
+ ForwardingAllocator(inFoundation.getAllocator(), "SBatchLoader::m_LoadingImagePool"))
+ , m_BatchPool(ForwardingAllocator(inFoundation.getAllocator(), "SBatchLoader::m_BatchPool"))
+ {
+ }
+
+ virtual ~SBatchLoader()
+ {
+ nvvector<TImageBatchId> theCancelledBatches(m_Foundation.getAllocator(), "~SBatchLoader");
+ for (TImageLoaderBatchMap::iterator theIter = m_Batches.begin(), theEnd = m_Batches.end();
+ theIter != theEnd; ++theIter) {
+ theIter->second->Cancel();
+ theCancelledBatches.push_back(theIter->second->m_BatchId);
+ }
+ for (QT3DSU32 idx = 0, end = theCancelledBatches.size(); idx < end; ++idx)
+ BlockUntilLoaded(theCancelledBatches[idx]);
+
+ QT3DS_ASSERT(m_Batches.size() == 0);
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ // Returns an ID to the load request. Request a block of images to be loaded.
+ // Also takes an image that the buffer system will return when requested for the given source
+ // paths
+ // until said path is loaded.
+ // An optional listener can be passed in to get callbacks about the batch.
+ TImageBatchId LoadImageBatch(NVConstDataRef<CRegisteredString> inSourcePaths,
+ CRegisteredString inImageTillLoaded,
+ IImageLoadListener *inListener,
+ NVRenderContextType contextType,
+ bool preferKTX, bool iblImages) override
+ {
+ if (inSourcePaths.size() == 0)
+ return 0;
+
+ TScopedLock __loaderLock(m_LoaderMutex);
+
+ TImageBatchId theBatchId = 0;
+
+ // Empty loop intentional to find an unused batch id.
+ for (theBatchId = m_NextBatchId; m_Batches.find(theBatchId) != m_Batches.end();
+ ++m_NextBatchId, theBatchId = m_NextBatchId) {
+ }
+
+ SImageLoaderBatch *theBatch(SImageLoaderBatch::CreateLoaderBatch(
+ *this, theBatchId, inSourcePaths, inImageTillLoaded, inListener, contextType,
+ preferKTX, iblImages));
+ if (theBatch) {
+ m_Batches.insert(eastl::make_pair(theBatchId, theBatch));
+ return theBatchId;
+ }
+ return 0;
+ }
+
+ void CancelImageBatchLoading(TImageBatchId inBatchId) override
+ {
+ SImageLoaderBatch *theBatch(GetBatch(inBatchId));
+ if (theBatch)
+ theBatch->Cancel();
+ }
+
+ // Blocks if the image is currently in-flight
+ void CancelImageLoading(CRegisteredString inSourcePath) override
+ {
+ TScopedLock __loaderLock(m_LoaderMutex);
+ TSourcePathToBatchMap::iterator theIter = m_SourcePathToBatches.find(inSourcePath);
+ if (theIter != m_SourcePathToBatches.end()) {
+ TImageBatchId theBatchId = theIter->second;
+ TImageLoaderBatchMap::iterator theBatchIter = m_Batches.find(theBatchId);
+ if (theBatchIter != m_Batches.end())
+ theBatchIter->second->Cancel(inSourcePath);
+ }
+ }
+
+ SImageLoaderBatch *GetBatch(TImageBatchId inId)
+ {
+ TScopedLock __loaderLock(m_LoaderMutex);
+ TImageLoaderBatchMap::iterator theIter = m_Batches.find(inId);
+ if (theIter != m_Batches.end())
+ return theIter->second;
+ return NULL;
+ }
+
+ void BlockUntilLoaded(TImageBatchId inId) override
+ {
+ for (SImageLoaderBatch *theBatch = GetBatch(inId); theBatch; theBatch = GetBatch(inId)) {
+ // Only need to block if images aren't loaded. Don't need to block if they aren't
+ // finalized.
+ if (!theBatch->IsLoadingFinished()) {
+ theBatch->m_LoadEvent.wait(200);
+ theBatch->m_LoadEvent.reset();
+ }
+ BeginFrame(true);
+ }
+ }
+ void ImageLoaded(SLoadingImage &inImage, SLoadedTexture *inTexture)
+ {
+ TScopedLock __loaderLock(m_LoaderMutex);
+ m_LoadedImages.push_back(
+ SBatchLoadedImage(inImage.m_SourcePath, inTexture, *inImage.m_Batch));
+ inImage.m_Batch->IncrementLoadedImageCount();
+ inImage.m_Batch->m_LoadEvent.set();
+ }
+ // These are called by the render context, users don't need to call this.
+ void BeginFrame(bool firstFrame) override
+ {
+ TScopedLock __loaderLock(m_LoaderMutex);
+ // Pass 1 - send out all image loaded signals
+ for (QT3DSU32 idx = 0, end = m_LoadedImages.size(); idx < end; ++idx) {
+
+ m_SourcePathToBatches.erase(m_LoadedImages[idx].m_SourcePath);
+ m_LoadedImages[idx].Finalize(m_BufferManager);
+ m_LoadedImages[idx].m_Batch->IncrementFinalizedImageCount();
+ if (m_LoadedImages[idx].m_Batch->IsFinalizedFinished())
+ m_FinishedBatches.push_back(m_LoadedImages[idx].m_Batch->m_BatchId);
+ if (!firstFrame)
+ break;
+ }
+ if (firstFrame)
+ m_LoadedImages.clear();
+ else if (m_LoadedImages.size())
+ m_LoadedImages.erase(m_LoadedImages.begin());
+ // pass 2 - clean up any existing batches.
+ for (QT3DSU32 idx = 0, end = m_FinishedBatches.size(); idx < end; ++idx) {
+ TImageLoaderBatchMap::iterator theIter = m_Batches.find(m_FinishedBatches[idx]);
+ if (theIter != m_Batches.end()) {
+ SImageLoaderBatch *theBatch = theIter->second;
+ if (theBatch->m_LoadListener)
+ theBatch->m_LoadListener->OnImageBatchComplete(theBatch->m_BatchId);
+ m_Batches.erase(m_FinishedBatches[idx]);
+ theBatch->~SImageLoaderBatch();
+ m_BatchPool.deallocate(theBatch);
+ }
+ }
+ m_FinishedBatches.clear();
+ }
+
+ void EndFrame() override {}
+};
+
+void SLoadingImage::Setup(SImageLoaderBatch &inBatch)
+{
+ m_Batch = &inBatch;
+ m_TaskId = inBatch.m_Loader.m_ThreadPool.AddTask(this, LoadImage, TaskCancelled);
+}
+
+void SLoadingImage::LoadImage(void *inImg)
+{
+ SLoadingImage *theThis = reinterpret_cast<SLoadingImage *>(inImg);
+ SStackPerfTimer theTimer(theThis->m_Batch->m_Loader.m_PerfTimer, "Image Decompression");
+ if (theThis->m_Batch->m_Loader.m_BufferManager.IsImageLoaded(theThis->m_SourcePath) == false) {
+ SLoadedTexture *theTexture = SLoadedTexture::Load(
+ theThis->m_SourcePath.c_str(), theThis->m_Batch->m_Loader.m_Foundation,
+ theThis->m_Batch->m_Loader.m_InputStreamFactory, true,
+ theThis->m_Batch->m_contextType,
+ theThis->m_Batch->m_preferKTX);
+ // if ( theTexture )
+ // theTexture->EnsureMultiplerOfFour( theThis->m_Batch->m_Loader.m_Foundation,
+ //theThis->m_SourcePath.c_str() );
+
+ theThis->m_Batch->m_Loader.ImageLoaded(*theThis, theTexture);
+ } else {
+ theThis->m_Batch->m_Loader.ImageLoaded(*theThis, NULL);
+ }
+}
+
+void SLoadingImage::TaskCancelled(void *inImg)
+{
+ SLoadingImage *theThis = reinterpret_cast<SLoadingImage *>(inImg);
+ theThis->m_Batch->m_Loader.ImageLoaded(*theThis, NULL);
+}
+
+bool SBatchLoadedImage::Finalize(IBufferManager &inMgr)
+{
+ if (m_Texture) {
+ eastl::string thepath(m_SourcePath);
+ bool isIBL = this->m_Batch->m_ibl;
+ inMgr.LoadRenderImage(m_SourcePath, *m_Texture, false, isIBL);
+ inMgr.UnaliasImagePath(m_SourcePath);
+ }
+ if (m_Batch->m_LoadListener)
+ m_Batch->m_LoadListener->OnImageLoadComplete(
+ m_SourcePath, m_Texture ? ImageLoadResult::Succeeded : ImageLoadResult::Failed);
+
+ if (m_Texture) {
+ m_Texture->release();
+ return true;
+ }
+
+ return false;
+}
+
+SImageLoaderBatch *
+SImageLoaderBatch::CreateLoaderBatch(SBatchLoader &inLoader, TImageBatchId inBatchId,
+ NVConstDataRef<CRegisteredString> inSourcePaths,
+ CRegisteredString inImageTillLoaded,
+ IImageLoadListener *inListener,
+ NVRenderContextType contextType,
+ bool preferKTX, bool iblImages)
+{
+ TLoadingImageList theImages;
+ QT3DSU32 theLoadingImageCount = 0;
+ for (QT3DSU32 idx = 0, end = inSourcePaths.size(); idx < end; ++idx) {
+ CRegisteredString theSourcePath(inSourcePaths[idx]);
+
+ if (theSourcePath.IsValid() == false)
+ continue;
+
+ if (inLoader.m_BufferManager.IsImageLoaded(theSourcePath))
+ continue;
+
+ eastl::pair<SBatchLoader::TSourcePathToBatchMap::iterator, bool> theInserter =
+ inLoader.m_SourcePathToBatches.insert(eastl::make_pair(inSourcePaths[idx], inBatchId));
+
+ // If the loader has already seen this image.
+ if (theInserter.second == false)
+ continue;
+
+ if (inImageTillLoaded.IsValid()) {
+ // Alias the image so any further requests for this source path will result in
+ // the default images (image till loaded).
+ bool aliasSuccess =
+ inLoader.m_BufferManager.AliasImagePath(theSourcePath, inImageTillLoaded, true);
+ (void)aliasSuccess;
+ QT3DS_ASSERT(aliasSuccess);
+ }
+
+ theImages.push_front(
+ *inLoader.m_LoadingImagePool.construct(theSourcePath, __FILE__, __LINE__));
+ ++theLoadingImageCount;
+ }
+ if (theImages.empty() == false) {
+ SImageLoaderBatch *theBatch =
+ (SImageLoaderBatch *)inLoader.m_BatchPool.allocate(__FILE__, __LINE__);
+ new (theBatch)
+ SImageLoaderBatch(inLoader, inListener, theImages, inBatchId, theLoadingImageCount,
+ contextType, preferKTX, iblImages);
+ return theBatch;
+ }
+ return NULL;
+}
+
+SImageLoaderBatch::SImageLoaderBatch(SBatchLoader &inLoader, IImageLoadListener *inLoadListener,
+ const TLoadingImageList &inImageList, TImageBatchId inBatchId,
+ QT3DSU32 inImageCount, NVRenderContextType contextType,
+ bool preferKTX, bool ibl)
+ : m_Loader(inLoader)
+ , m_LoadListener(inLoadListener)
+ , m_LoadEvent(inLoader.m_Foundation.getAllocator())
+ , m_LoadMutex(inLoader.m_Foundation.getAllocator())
+ , m_Images(inImageList)
+ , m_BatchId(inBatchId)
+ , m_LoadedOrCanceledImageCount(0)
+ , m_FinalizedImageCount(0)
+ , m_NumImages(inImageCount)
+ , m_contextType(contextType)
+ , m_preferKTX(preferKTX)
+ , m_ibl(ibl)
+{
+ for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end;
+ ++iter) {
+ iter->Setup(*this);
+ }
+}
+
+SImageLoaderBatch::~SImageLoaderBatch()
+{
+ for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end;
+ ++iter) {
+ TLoadingImageList::iterator temp(iter);
+ ++iter;
+ m_Loader.m_LoadingImagePool.deallocate(temp.m_Obj);
+ }
+}
+
+void SImageLoaderBatch::Cancel()
+{
+ for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end;
+ ++iter)
+ m_Loader.m_ThreadPool.CancelTask(iter->m_TaskId);
+}
+
+void SImageLoaderBatch::Cancel(CRegisteredString inSourcePath)
+{
+ for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end;
+ ++iter) {
+ if (iter->m_SourcePath == inSourcePath) {
+ m_Loader.m_ThreadPool.CancelTask(iter->m_TaskId);
+ break;
+ }
+ }
+}
+}
+
+IImageBatchLoader &IImageBatchLoader::CreateBatchLoader(NVFoundationBase &inFoundation,
+ IInputStreamFactory &inFactory,
+ IBufferManager &inBufferManager,
+ IThreadPool &inThreadPool,
+ IPerfTimer &inTimer)
+{
+ return *QT3DS_NEW(inFoundation.getAllocator(),
+ SBatchLoader)(inFoundation, inFactory, inBufferManager, inThreadPool, inTimer);
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h
new file mode 100644
index 0000000..2805940
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_RENDER_THREADED_IMAGE_LOADER_H
+#define QT3DS_RENDER_THREADED_IMAGE_LOADER_H
+#include "Qt3DSRender.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+
+namespace qt3ds {
+namespace render {
+ struct ImageLoadResult
+ {
+ enum Enum {
+ Succeeded,
+ Failed,
+ };
+ };
+
+ class IImageLoadListener : public NVRefCounted
+ {
+ protected:
+ virtual ~IImageLoadListener() {}
+
+ public:
+ virtual void OnImageLoadComplete(CRegisteredString inPath,
+ ImageLoadResult::Enum inResult) = 0;
+ virtual void OnImageBatchComplete(QT3DSU64 inBatch) = 0;
+ };
+
+ typedef QT3DSU32 TImageBatchId;
+
+ class IImageBatchLoader : public NVRefCounted
+ {
+ protected:
+ virtual ~IImageBatchLoader() {}
+
+ public:
+ // Returns an ID to the load request. Request a block of images to be loaded.
+ // Also takes an image that the buffer system will return when requested for the given
+ // source paths
+ // until said path is loaded.
+ // An optional listener can be passed in to get callbacks about the batch.
+ virtual TImageBatchId LoadImageBatch(NVConstDataRef<CRegisteredString> inSourcePaths,
+ CRegisteredString inImageTillLoaded,
+ IImageLoadListener *inListener,
+ NVRenderContextType type,
+ bool preferKTX, bool iblImages) = 0;
+ // Blocks if any of the images in the batch are in flight
+ virtual void CancelImageBatchLoading(TImageBatchId inBatchId) = 0;
+ // Blocks if the image is currently in-flight
+ virtual void CancelImageLoading(CRegisteredString inSourcePath) = 0;
+ // Block until every image in the batch is loaded.
+ virtual void BlockUntilLoaded(TImageBatchId inId) = 0;
+
+ // These are called by the render context, users don't need to call this.
+ virtual void BeginFrame(bool firstFrame) = 0;
+ virtual void EndFrame() = 0;
+
+ static IImageBatchLoader &CreateBatchLoader(NVFoundationBase &inFoundation,
+ IInputStreamFactory &inFactory,
+ IBufferManager &inBufferManager,
+ IThreadPool &inThreadPool, IPerfTimer &inTimer);
+ };
+}
+}
+
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp
new file mode 100644
index 0000000..1176273
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp
@@ -0,0 +1,715 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderLoadedTexture.h"
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "Qt3DSDMWindowsCompatibility.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "Qt3DSRenderImageScaler.h"
+#include "Qt3DSTextRenderer.h"
+#include <QImage>
+
+using namespace qt3ds::render;
+
+SLoadedTexture *SLoadedTexture::LoadQImage(const QString &inPath, QT3DSI32 flipVertical,
+ NVFoundationBase &fnd,
+ NVRenderContextType renderContextType)
+{
+ Q_UNUSED(flipVertical)
+ Q_UNUSED(renderContextType)
+ SLoadedTexture *retval(NULL);
+ NVAllocatorCallback &alloc(fnd.getAllocator());
+ QImage image(inPath);
+ const QImage::Format format = image.format();
+ switch (format) {
+ case QImage::Format_RGBA64:
+ image = image.convertToFormat(QImage::Format_RGBA8888);
+ break;
+ case QImage::Format_RGBX64:
+ image = image.convertToFormat(QImage::Format_RGBX8888);
+ break;
+ default:
+ break;
+ }
+ image = image.mirrored();
+ image = image.rgbSwapped();
+ retval = QT3DS_NEW(alloc, SLoadedTexture)(alloc);
+ retval->width = image.width();
+ retval->height = image.height();
+ retval->components = image.pixelFormat().channelCount();
+ retval->image = image;
+ retval->data = (void*)retval->image.bits();
+ retval->dataSizeInBytes = image.byteCount();
+ retval->setFormatFromComponents();
+ return retval;
+}
+
+
+namespace {
+
+/**
+ !!Large section of code ripped from FreeImage!!
+
+*/
+// ----------------------------------------------------------
+// Structures used by DXT textures
+// ----------------------------------------------------------
+typedef QT3DSU8 BYTE;
+typedef QT3DSU16 WORD;
+
+typedef struct tagColor8888
+{
+ BYTE b;
+ BYTE g;
+ BYTE r;
+ BYTE a;
+} Color8888;
+
+typedef struct tagColor565
+{
+ WORD b : 5;
+ WORD g : 6;
+ WORD r : 5;
+} Color565;
+
+typedef struct tagDXTColBlock
+{
+ Color565 colors[2];
+ BYTE row[4];
+} DXTColBlock;
+
+typedef struct tagDXTAlphaBlockExplicit
+{
+ WORD row[4];
+} DXTAlphaBlockExplicit;
+
+typedef struct tagDXTAlphaBlock3BitLinear
+{
+ BYTE alpha[2];
+ BYTE data[6];
+} DXTAlphaBlock3BitLinear;
+
+typedef struct tagDXT1Block
+{
+ DXTColBlock color;
+} DXT1Block;
+
+typedef struct tagDXT3Block
+{ // also used by dxt2
+ DXTAlphaBlockExplicit alpha;
+ DXTColBlock color;
+} DXT3Block;
+
+typedef struct tagDXT5Block
+{ // also used by dxt4
+ DXTAlphaBlock3BitLinear alpha;
+ DXTColBlock color;
+} DXT5Block;
+
+static void GetBlockColors(const DXTColBlock &block, Color8888 colors[4], bool isDXT1)
+{
+ int i;
+ for (i = 0; i < 2; i++) {
+ colors[i].a = 0xff;
+ colors[i].r = (BYTE)(block.colors[i].r * 0xff / 0x1f);
+ colors[i].g = (BYTE)(block.colors[i].g * 0xff / 0x3f);
+ colors[i].b = (BYTE)(block.colors[i].b * 0xff / 0x1f);
+ }
+
+ WORD *wCol = (WORD *)block.colors;
+ if (wCol[0] > wCol[1] || !isDXT1) {
+ // 4 color block
+ for (i = 0; i < 2; i++) {
+ colors[i + 2].a = 0xff;
+ colors[i + 2].r =
+ (BYTE)((WORD(colors[0].r) * (2 - i) + WORD(colors[1].r) * (1 + i)) / 3);
+ colors[i + 2].g =
+ (BYTE)((WORD(colors[0].g) * (2 - i) + WORD(colors[1].g) * (1 + i)) / 3);
+ colors[i + 2].b =
+ (BYTE)((WORD(colors[0].b) * (2 - i) + WORD(colors[1].b) * (1 + i)) / 3);
+ }
+ } else {
+ // 3 color block, number 4 is transparent
+ colors[2].a = 0xff;
+ colors[2].r = (BYTE)((WORD(colors[0].r) + WORD(colors[1].r)) / 2);
+ colors[2].g = (BYTE)((WORD(colors[0].g) + WORD(colors[1].g)) / 2);
+ colors[2].b = (BYTE)((WORD(colors[0].b) + WORD(colors[1].b)) / 2);
+
+ colors[3].a = 0x00;
+ colors[3].g = 0x00;
+ colors[3].b = 0x00;
+ colors[3].r = 0x00;
+ }
+}
+
+struct DXT_INFO_1
+{
+ typedef DXT1Block Block;
+ enum { isDXT1 = 1, bytesPerBlock = 8 };
+};
+
+struct DXT_INFO_3
+{
+ typedef DXT3Block Block;
+ enum { isDXT1 = 1, bytesPerBlock = 16 };
+};
+
+struct DXT_INFO_5
+{
+ typedef DXT5Block Block;
+ enum { isDXT1 = 1, bytesPerBlock = 16 };
+};
+
+template <class INFO>
+class DXT_BLOCKDECODER_BASE
+{
+protected:
+ Color8888 m_colors[4];
+ const typename INFO::Block *m_pBlock;
+ unsigned m_colorRow;
+
+public:
+ void Setup(const BYTE *pBlock)
+ {
+ m_pBlock = (const typename INFO::Block *)pBlock;
+ GetBlockColors(m_pBlock->color, m_colors, INFO::isDXT1);
+ }
+
+ void SetY(int y) { m_colorRow = m_pBlock->color.row[y]; }
+
+ void GetColor(int x, int y, Color8888 &color)
+ {
+ Q_UNUSED(y)
+ unsigned bits = (m_colorRow >> (x * 2)) & 3;
+ color = m_colors[bits];
+ std::swap(color.r, color.b);
+ }
+};
+
+class DXT_BLOCKDECODER_1 : public DXT_BLOCKDECODER_BASE<DXT_INFO_1>
+{
+public:
+ typedef DXT_INFO_1 INFO;
+};
+
+class DXT_BLOCKDECODER_3 : public DXT_BLOCKDECODER_BASE<DXT_INFO_3>
+{
+public:
+ typedef DXT_BLOCKDECODER_BASE<DXT_INFO_3> base;
+ typedef DXT_INFO_3 INFO;
+
+protected:
+ unsigned m_alphaRow;
+
+public:
+ void SetY(int y)
+ {
+ base::SetY(y);
+ m_alphaRow = m_pBlock->alpha.row[y];
+ }
+
+ void GetColor(int x, int y, Color8888 &color)
+ {
+ base::GetColor(x, y, color);
+ const unsigned bits = (m_alphaRow >> (x * 4)) & 0xF;
+ color.a = (BYTE)((bits * 0xFF) / 0xF);
+ }
+};
+
+class DXT_BLOCKDECODER_5 : public DXT_BLOCKDECODER_BASE<DXT_INFO_5>
+{
+public:
+ typedef DXT_BLOCKDECODER_BASE<DXT_INFO_5> base;
+ typedef DXT_INFO_5 INFO;
+
+protected:
+ unsigned m_alphas[8];
+ unsigned m_alphaBits;
+ int m_offset;
+
+public:
+ void Setup(const BYTE *pBlock)
+ {
+ base::Setup(pBlock);
+
+ const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha;
+ m_alphas[0] = block.alpha[0];
+ m_alphas[1] = block.alpha[1];
+ if (m_alphas[0] > m_alphas[1]) {
+ // 8 alpha block
+ for (int i = 0; i < 6; i++) {
+ m_alphas[i + 2] = ((6 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 3) / 7;
+ }
+ } else {
+ // 6 alpha block
+ for (int i = 0; i < 4; i++) {
+ m_alphas[i + 2] = ((4 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 2) / 5;
+ }
+ m_alphas[6] = 0;
+ m_alphas[7] = 0xFF;
+ }
+ }
+
+ void SetY(int y)
+ {
+ base::SetY(y);
+ int i = y / 2;
+ const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha;
+ m_alphaBits = unsigned(block.data[0 + i * 3]) | (unsigned(block.data[1 + i * 3]) << 8)
+ | (unsigned(block.data[2 + i * 3]) << 16);
+ m_offset = (y & 1) * 12;
+ }
+
+ void GetColor(int x, int y, Color8888 &color)
+ {
+ base::GetColor(x, y, color);
+ unsigned bits = (m_alphaBits >> (x * 3 + m_offset)) & 7;
+ color.a = (BYTE)m_alphas[bits];
+ std::swap(color.r, color.b);
+ }
+};
+
+template <class DECODER>
+void DecodeDXTBlock(BYTE *dstData, const BYTE *srcBlock, long dstPitch, int bw, int bh)
+{
+ DECODER decoder;
+ decoder.Setup(srcBlock);
+ for (int y = 0; y < bh; y++) {
+ // Note that this assumes the pointer is pointing to the *last* valid start
+ // row.
+ BYTE *dst = dstData - y * dstPitch;
+ decoder.SetY(y);
+ for (int x = 0; x < bw; x++) {
+ decoder.GetColor(x, y, (Color8888 &)*dst);
+ dst += 4;
+ }
+ }
+}
+
+struct STextureDataWriter
+{
+ QT3DSU32 m_Width;
+ QT3DSU32 m_Height;
+ QT3DSU32 m_Stride;
+ QT3DSU32 m_NumComponents;
+ STextureData &m_TextureData;
+ STextureDataWriter(QT3DSU32 w, QT3DSU32 h, bool hasA, STextureData &inTd, NVAllocatorCallback &alloc)
+ : m_Width(w)
+ , m_Height(h)
+ , m_Stride(hasA ? m_Width * 4 : m_Width * 3)
+ , m_NumComponents(hasA ? 4 : 3)
+ , m_TextureData(inTd)
+ {
+ QT3DSU32 dataSize = m_Stride * m_Height;
+ if (dataSize > m_TextureData.dataSizeInBytes) {
+ alloc.deallocate(m_TextureData.data);
+ m_TextureData.data =
+ alloc.allocate(dataSize, "SLoadedTexture::DecompressDXTImage", __FILE__, __LINE__);
+ m_TextureData.dataSizeInBytes = dataSize;
+ }
+ memZero(m_TextureData.data, m_TextureData.dataSizeInBytes);
+ m_TextureData.format = hasA ? NVRenderTextureFormats::RGBA8 : NVRenderTextureFormats::RGB8;
+ }
+
+ void WritePixel(QT3DSU32 X, QT3DSU32 Y, QT3DSU8 *pixelData)
+ {
+ if (X < m_Width && Y < m_Height) {
+ char *textureData = reinterpret_cast<char *>(m_TextureData.data);
+ QT3DSU32 offset = Y * m_Stride + X * m_NumComponents;
+
+ for (QT3DSU32 idx = 0; idx < m_NumComponents; ++idx)
+ QT3DS_ASSERT(textureData[offset + idx] == 0);
+
+ memCopy(textureData + offset, pixelData, m_NumComponents);
+ }
+ }
+
+ // Incoming pixels are assumed to be RGBA or RGBX, 32 bit in any case
+ void WriteBlock(QT3DSU32 X, QT3DSU32 Y, QT3DSU32 width, QT3DSU32 height, QT3DSU8 *pixelData)
+ {
+ QT3DSU32 offset = 0;
+ for (QT3DSU32 yidx = 0; yidx < height; ++yidx) {
+ for (QT3DSU32 xidx = 0; xidx < width; ++xidx, offset += 4) {
+ WritePixel(X + xidx, Y + (height - yidx - 1), pixelData + offset);
+ }
+ }
+ }
+ bool Finished() { return false; }
+};
+
+struct STextureAlphaScanner
+{
+ bool &m_Alpha;
+
+ STextureAlphaScanner(bool &inAlpha)
+ : m_Alpha(inAlpha)
+ {
+ }
+
+ void WriteBlock(QT3DSU32 X, QT3DSU32 Y, QT3DSU32 width, QT3DSU32 height, QT3DSU8 *pixelData)
+ {
+ Q_UNUSED(X)
+ Q_UNUSED(Y)
+ QT3DSU32 offset = 0;
+ for (QT3DSU32 yidx = 0; yidx < height; ++yidx) {
+ for (QT3DSU32 xidx = 0; xidx < width; ++xidx, offset += 4) {
+ if (pixelData[offset + 3] < 255)
+ m_Alpha = true;
+ }
+ }
+ }
+
+ // If we detect alpha we can stop right there.
+ bool Finished() { return m_Alpha; }
+};
+// Scan the dds image's mipmap 0 level for alpha.
+template <class DECODER, class TWriterType>
+static void DecompressDDS(void *inSrc, QT3DSU32 inDataSize, QT3DSU32 inWidth, QT3DSU32 inHeight,
+ TWriterType ioWriter)
+{
+ typedef typename DECODER::INFO INFO;
+ typedef typename INFO::Block Block;
+ (void)inDataSize;
+
+ const QT3DSU8 *pbSrc = (const QT3DSU8 *)inSrc;
+ // Each DX block is composed of 16 pixels. Free image decodes those
+ // pixels into a 4x4 block of data.
+ QT3DSU8 pbDstData[4 * 4 * 4];
+ // The decoder decodes backwards
+ // So we need to point to the last line.
+ QT3DSU8 *pbDst = pbDstData + 48;
+
+ int width = (int)inWidth;
+ int height = (int)inHeight;
+ int lineStride = 16;
+ for (int y = 0; y < height && ioWriter.Finished() == false; y += 4) {
+ int yPixels = NVMin(height - y, 4);
+ for (int x = 0; x < width && ioWriter.Finished() == false; x += 4) {
+ int xPixels = NVMin(width - x, 4);
+ DecodeDXTBlock<DECODER>(pbDst, pbSrc, lineStride, xPixels, yPixels);
+ pbSrc += INFO::bytesPerBlock;
+ ioWriter.WriteBlock(x, y, xPixels, yPixels, pbDstData);
+ }
+ }
+}
+
+bool ScanDDSForAlpha(Qt3DSDDSImage *dds)
+{
+ bool hasAlpha = false;
+ switch (dds->format) {
+ case qt3ds::render::NVRenderTextureFormats::RGBA_DXT1:
+ DecompressDDS<DXT_BLOCKDECODER_1>(dds->data[0], dds->size[0], dds->mipwidth[0],
+ dds->mipheight[0], STextureAlphaScanner(hasAlpha));
+ break;
+ case qt3ds::render::NVRenderTextureFormats::RGBA_DXT3:
+ DecompressDDS<DXT_BLOCKDECODER_3>(dds->data[0], dds->size[0], dds->mipwidth[0],
+ dds->mipheight[0], STextureAlphaScanner(hasAlpha));
+ break;
+ case qt3ds::render::NVRenderTextureFormats::RGBA_DXT5:
+ DecompressDDS<DXT_BLOCKDECODER_5>(dds->data[0], dds->size[0], dds->mipwidth[0],
+ dds->mipheight[0], STextureAlphaScanner(hasAlpha));
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ return hasAlpha;
+}
+
+bool ScanImageForAlpha(const void *inData, QT3DSU32 inWidth, QT3DSU32 inHeight, QT3DSU32 inPixelSizeInBytes,
+ QT3DSU8 inAlphaSizeInBits)
+{
+ const QT3DSU8 *rowPtr = reinterpret_cast<const QT3DSU8 *>(inData);
+ bool hasAlpha = false;
+ if (inAlphaSizeInBits == 0)
+ return hasAlpha;
+ if (inPixelSizeInBytes != 2 && inPixelSizeInBytes != 4) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+ if (inAlphaSizeInBits > 8) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+
+ QT3DSU32 alphaRightShift = inPixelSizeInBytes * 8 - inAlphaSizeInBits;
+ QT3DSU32 maxAlphaValue = (1 << inAlphaSizeInBits) - 1;
+
+ for (QT3DSU32 rowIdx = 0; rowIdx < inHeight && hasAlpha == false; ++rowIdx) {
+ for (QT3DSU32 idx = 0; idx < inWidth && hasAlpha == false;
+ ++idx, rowPtr += inPixelSizeInBytes) {
+ QT3DSU32 pixelValue = 0;
+ if (inPixelSizeInBytes == 2)
+ pixelValue = *(reinterpret_cast<const QT3DSU16 *>(rowPtr));
+ else
+ pixelValue = *(reinterpret_cast<const QT3DSU32 *>(rowPtr));
+ pixelValue = pixelValue >> alphaRightShift;
+ if (pixelValue < maxAlphaValue)
+ hasAlpha = true;
+ }
+ }
+ return hasAlpha;
+}
+}
+
+SLoadedTexture::~SLoadedTexture()
+{
+ if (dds) {
+ if (dds->dataBlock)
+ QT3DS_FREE(m_Allocator, dds->dataBlock);
+
+ QT3DS_FREE(m_Allocator, dds);
+ } else if (data && image.byteCount() <= 0) {
+ m_Allocator.deallocate(data);
+ }
+ if (m_Palette)
+ m_Allocator.deallocate(m_Palette);
+ if (m_TransparencyTable)
+ m_Allocator.deallocate(m_TransparencyTable);
+}
+
+void SLoadedTexture::release()
+{
+ NVAllocatorCallback *theAllocator(&m_Allocator);
+ this->~SLoadedTexture();
+ theAllocator->deallocate(this);
+}
+
+bool SLoadedTexture::ScanForTransparency()
+{
+ switch (format) {
+ case NVRenderTextureFormats::SRGB8A8:
+ case NVRenderTextureFormats::RGBA8:
+ if (!data) { // dds
+ return true;
+ } else {
+ return ScanImageForAlpha(data, width, height, 4, 8);
+ }
+ break;
+ // Scan the image.
+ case NVRenderTextureFormats::SRGB8:
+ case NVRenderTextureFormats::RGB8:
+ return false;
+ break;
+ case NVRenderTextureFormats::RGB565:
+ return false;
+ break;
+ case NVRenderTextureFormats::RGBA5551:
+ if (!data) { // dds
+ return true;
+ } else {
+ return ScanImageForAlpha(data, width, height, 2, 1);
+ }
+ break;
+ case NVRenderTextureFormats::Alpha8:
+ return true;
+ break;
+ case NVRenderTextureFormats::Luminance8:
+ return false;
+ break;
+ case NVRenderTextureFormats::LuminanceAlpha8:
+ if (!data) { // dds
+ return true;
+ } else {
+ return ScanImageForAlpha(data, width, height, 2, 8);
+ }
+ break;
+ case NVRenderTextureFormats::RGB_DXT1:
+ return false;
+ break;
+ case NVRenderTextureFormats::RGBA_DXT3:
+ case NVRenderTextureFormats::RGBA_DXT1:
+ case NVRenderTextureFormats::RGBA_DXT5:
+ if (dds) {
+ return ScanDDSForAlpha(dds);
+ } else {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+ break;
+ case NVRenderTextureFormats::RGB9E5:
+ return false;
+ break;
+ case NVRenderTextureFormats::RG32F:
+ case NVRenderTextureFormats::RGB32F:
+ case NVRenderTextureFormats::RGBA16F:
+ case NVRenderTextureFormats::RGBA32F:
+ // PKC TODO : For now, since IBL will be the main consumer, we'll just pretend there's no
+ // alpha.
+ // Need to do a proper scan down the line, but doing it for floats is a little different
+ // from
+ // integer scans.
+ return false;
+ break;
+ default:
+ break;
+ }
+ QT3DS_ASSERT(false);
+ return false;
+}
+
+void SLoadedTexture::EnsureMultiplerOfFour(NVFoundationBase &inFoundation, const char *inPath)
+{
+ if (width % 4 || height % 4) {
+ qCWarning(PERF_WARNING,
+ "Image %s has non multiple of four width or height; perf hit for scaling", inPath);
+ if (data) {
+ QT3DSU32 newWidth = ITextRenderer::NextMultipleOf4(width);
+ QT3DSU32 newHeight = ITextRenderer::NextMultipleOf4(height);
+ QT3DSU32 newDataSize = newWidth * newHeight * components;
+ NVAllocatorCallback &theAllocator(inFoundation.getAllocator());
+ QT3DSU8 *newData = (QT3DSU8 *)(theAllocator.allocate(newDataSize, "Scaled Image Data",
+ __FILE__, __LINE__));
+ CImageScaler theScaler(theAllocator);
+ if (components == 4) {
+ theScaler.FastExpandRowsAndColumns((unsigned char *)data, width, height, newData,
+ newWidth, newHeight);
+ } else
+ theScaler.ExpandRowsAndColumns((unsigned char *)data, width, height, newData,
+ newWidth, newHeight, components);
+
+ theAllocator.deallocate(data);
+ data = newData;
+ width = newWidth;
+ height = newHeight;
+ dataSizeInBytes = newDataSize;
+ }
+ }
+}
+
+STextureData SLoadedTexture::DecompressDXTImage(int inMipMapIdx, STextureData *inOptLastImage)
+{
+ STextureData retval;
+ if (inOptLastImage)
+ retval = *inOptLastImage;
+
+ if (dds == NULL || inMipMapIdx >= dds->numMipmaps) {
+ QT3DS_ASSERT(false);
+ ReleaseDecompressedTexture(retval);
+ return STextureData();
+ }
+ char *srcData = (char *)dds->data[inMipMapIdx];
+ int srcDataSize = dds->size[inMipMapIdx];
+ QT3DSU32 imgWidth = (QT3DSU32)dds->mipwidth[inMipMapIdx];
+ QT3DSU32 imgHeight = (QT3DSU32)dds->mipheight[inMipMapIdx];
+
+ switch (format) {
+ case NVRenderTextureFormats::RGB_DXT1:
+ DecompressDDS<DXT_BLOCKDECODER_1>(
+ srcData, srcDataSize, imgWidth, imgHeight,
+ STextureDataWriter(imgWidth, imgHeight, false, retval, m_Allocator));
+ break;
+ case NVRenderTextureFormats::RGBA_DXT1:
+ DecompressDDS<DXT_BLOCKDECODER_1>(
+ srcData, srcDataSize, imgWidth, imgHeight,
+ STextureDataWriter(imgWidth, imgHeight, true, retval, m_Allocator));
+ break;
+ case NVRenderTextureFormats::RGBA_DXT3:
+ DecompressDDS<DXT_BLOCKDECODER_3>(
+ srcData, srcDataSize, imgWidth, imgHeight,
+ STextureDataWriter(imgWidth, imgHeight, true, retval, m_Allocator));
+ break;
+ case NVRenderTextureFormats::RGBA_DXT5:
+ DecompressDDS<DXT_BLOCKDECODER_5>(
+ srcData, srcDataSize, imgWidth, imgHeight,
+ STextureDataWriter(imgWidth, imgHeight, true, retval, m_Allocator));
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ return retval;
+}
+
+void SLoadedTexture::ReleaseDecompressedTexture(STextureData inImage)
+{
+ if (inImage.data)
+ m_Allocator.deallocate(inImage.data);
+}
+
+#ifndef EA_PLATFORM_WINDOWS
+#define stricmp strcasecmp
+#endif
+
+SLoadedTexture *SLoadedTexture::Load(const QString &inPath, NVFoundationBase &inFoundation,
+ IInputStreamFactory &inFactory, bool inFlipY,
+ NVRenderContextType renderContextType, bool preferKTX)
+{
+ if (inPath.isEmpty())
+ return nullptr;
+
+ // Check KTX path first
+ QString path = inPath;
+ QString ktxSource = inPath;
+ if (preferKTX) {
+ ktxSource = ktxSource.left(ktxSource.lastIndexOf(QLatin1Char('.')));
+ ktxSource.append(QLatin1String(".ktx"));
+ }
+
+ SLoadedTexture *theLoadedImage = nullptr;
+ // We will get invalid error logs of files not found if we don't force quiet mode
+ // If the file is actually missing, it will be logged later (loaded image is null)
+ NVScopedRefCounted<IRefCountedInputStream> theStream(
+ inFactory.GetStreamForFile(preferKTX ? ktxSource : inPath, true));
+ if (!theStream.mPtr) {
+ if (preferKTX)
+ theStream = inFactory.GetStreamForFile(inPath, true);
+ else
+ return nullptr;
+ } else {
+ path = ktxSource;
+ }
+ QString fileName;
+ inFactory.GetPathForFile(path, fileName, true);
+ if (theStream.mPtr && path.size() > 3) {
+ if (path.endsWith(QLatin1String("png"), Qt::CaseInsensitive)
+ || path.endsWith(QLatin1String("jpg"), Qt::CaseInsensitive)
+ || path.endsWith(QLatin1String("peg"), Qt::CaseInsensitive)) {
+ theLoadedImage = LoadQImage(fileName, inFlipY, inFoundation, renderContextType);
+ } else if (path.endsWith(QLatin1String("dds"), Qt::CaseInsensitive)) {
+ theLoadedImage = LoadDDS(*theStream, inFlipY, inFoundation, renderContextType);
+ } else if (path.endsWith(QLatin1String("gif"), Qt::CaseInsensitive)) {
+ theLoadedImage = LoadGIF(*theStream, !inFlipY, inFoundation, renderContextType);
+ } else if (path.endsWith(QLatin1String("bmp"), Qt::CaseInsensitive)) {
+ theLoadedImage = LoadBMP(*theStream, !inFlipY, inFoundation, renderContextType);
+ } else if (path.endsWith(QLatin1String("hdr"), Qt::CaseInsensitive)) {
+ theLoadedImage = LoadHDR(*theStream, inFoundation, renderContextType);
+ } else if (path.endsWith(QLatin1String("ktx"), Qt::CaseInsensitive)) {
+ theLoadedImage = LoadKTX(*theStream, inFlipY, inFoundation, renderContextType);
+ } else {
+ qCWarning(INTERNAL_ERROR, "Unrecognized image extension: %s", qPrintable(inPath));
+ }
+ }
+
+ return theLoadedImage;
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h
new file mode 100644
index 0000000..f019400
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_RENDER_LOADED_TEXTURE_H
+#define QT3DS_RENDER_LOADED_TEXTURE_H
+#include "Qt3DSRender.h"
+#include "foundation/Qt3DSSimpleTypes.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+#include "Qt3DSRenderLoadedTextureDDS.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include <QImage>
+
+namespace qt3ds {
+namespace foundation {
+ class ISeekableIOStream;
+ class IInStream;
+}
+}
+
+namespace qt3ds {
+namespace render {
+
+ class IInputStreamFactory;
+
+ struct STextureData
+ {
+ void *data;
+ QT3DSU32 dataSizeInBytes;
+ qt3ds::render::NVRenderTextureFormats::Enum format;
+ STextureData()
+ : data(NULL)
+ , dataSizeInBytes(0)
+ , format(qt3ds::render::NVRenderTextureFormats::Unknown)
+ {
+ }
+ };
+ struct ExtendedTextureFormats
+ {
+ enum Enum {
+ NoExtendedFormat = 0,
+ Palettized,
+ CustomRGB,
+ };
+ };
+ // Utility class used for loading image data from disk.
+ // Supports jpg, png, and dds.
+ struct SLoadedTexture : public NVReleasable
+ {
+ private:
+ ~SLoadedTexture();
+
+ public:
+ NVAllocatorCallback &m_Allocator;
+ QT3DSI32 width;
+ QT3DSI32 height;
+ QT3DSI32 components;
+ void *data;
+ QImage image;
+ QT3DSU32 dataSizeInBytes;
+ qt3ds::render::NVRenderTextureFormats::Enum format;
+ Qt3DSDDSImage *dds;
+ ExtendedTextureFormats::Enum m_ExtendedFormat;
+ // Used for palettized images.
+ void *m_Palette;
+ QT3DSI32 m_CustomMasks[3];
+ int m_BitCount;
+ char8_t m_BackgroundColor[3];
+ uint8_t *m_TransparencyTable;
+ int32_t m_TransparentPaletteIndex;
+
+ SLoadedTexture(NVAllocatorCallback &inAllocator)
+ : m_Allocator(inAllocator)
+ , width(0)
+ , height(0)
+ , components(0)
+ , data(NULL)
+ , image(0)
+ , dataSizeInBytes(0)
+ , format(qt3ds::render::NVRenderTextureFormats::RGBA8)
+ , dds(NULL)
+ , m_ExtendedFormat(ExtendedTextureFormats::NoExtendedFormat)
+ , m_Palette(NULL)
+ , m_BitCount(0)
+ , m_TransparencyTable(NULL)
+ , m_TransparentPaletteIndex(-1)
+ {
+ m_CustomMasks[0] = 0;
+ m_CustomMasks[1] = 0;
+ m_CustomMasks[2] = 0;
+ m_BackgroundColor[0] = 0;
+ m_BackgroundColor[1] = 0;
+ m_BackgroundColor[2] = 0;
+ }
+ void setFormatFromComponents()
+ {
+ switch (components) {
+ case 1: // undefined, but in this context probably luminance
+ format = qt3ds::render::NVRenderTextureFormats::Luminance8;
+ break;
+ case 2:
+ format = qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8;
+ break;
+ case 3:
+ format = qt3ds::render::NVRenderTextureFormats::RGB8;
+ break;
+
+ default:
+ // fallthrough intentional
+ case 4:
+ format = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ break;
+ }
+ }
+
+ void EnsureMultiplerOfFour(NVFoundationBase &inFoundation, const char *inPath);
+ // Returns true if this image has a pixel less than 255.
+ bool ScanForTransparency();
+
+ // Be sure to call this or risk leaking an enormous amount of memory
+ void release() override;
+
+ // Not all video cards support dxt compression. Giving the last image allows
+ // this object to potentially reuse the memory
+ STextureData DecompressDXTImage(int inMipMapIdx, STextureData *inOptLastImage = NULL);
+ void ReleaseDecompressedTexture(STextureData inImage);
+
+ static SLoadedTexture *Load(const QString &inPath, NVFoundationBase &inAllocator,
+ IInputStreamFactory &inFactory, bool inFlipY = true,
+ NVRenderContextType renderContextType
+ = NVRenderContextValues::NullContext, bool preferKTX = false);
+ static SLoadedTexture *LoadDDS(IInStream &inStream, QT3DSI32 flipVertical,
+ NVFoundationBase &fnd,
+ NVRenderContextType renderContextType);
+ static SLoadedTexture *LoadKTX(IInStream &inStream, QT3DSI32 flipVertical,
+ NVFoundationBase &fnd,
+ NVRenderContextType renderContextType);
+ static SLoadedTexture *LoadBMP(ISeekableIOStream &inStream, bool inFlipY,
+ NVFoundationBase &inFnd,
+ NVRenderContextType renderContextType);
+ static SLoadedTexture *LoadGIF(ISeekableIOStream &inStream, bool inFlipY,
+ NVFoundationBase &inFnd,
+ NVRenderContextType renderContextType);
+ static SLoadedTexture *LoadHDR(ISeekableIOStream &inStream, NVFoundationBase &inFnd,
+ NVRenderContextType renderContextType);
+
+ static SLoadedTexture *LoadQImage(const QString &inPath, QT3DSI32 flipVertical,
+ NVFoundationBase &fnd,
+ NVRenderContextType renderContextType);
+
+ private:
+ // Implemented in the bmp loader.
+ void FreeImagePostProcess(bool inFlipY);
+ };
+}
+}
+
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp
new file mode 100644
index 0000000..29e75a8
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp
@@ -0,0 +1,1262 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+// ==========================================================
+// BMP Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Martin Weber (martweb@gmx.net)
+// - Herve Drolon (drolon@infonie.fr)
+// - Michal Novotny (michal@etc.cz)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "Qt3DSRenderLoadedTextureFreeImageCompat.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+static const BYTE RLE_COMMAND = 0;
+static const BYTE RLE_ENDOFLINE = 0;
+static const BYTE RLE_ENDOFBITMAP = 1;
+static const BYTE RLE_DELTA = 2;
+
+static const BYTE BI_RGB = 0;
+static const BYTE BI_RLE8 = 1;
+static const BYTE BI_RLE4 = 2;
+static const BYTE BI_BITFIELDS = 3;
+
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagBITMAPCOREHEADER
+{
+ DWORD bcSize;
+ WORD bcWidth;
+ WORD bcHeight;
+ WORD bcPlanes;
+ WORD bcBitCnt;
+} BITMAPCOREHEADER, *PBITMAPCOREHEADER;
+
+typedef struct tagBITMAPINFOOS2_1X_HEADER
+{
+ DWORD biSize;
+ WORD biWidth;
+ WORD biHeight;
+ WORD biPlanes;
+ WORD biBitCount;
+} BITMAPINFOOS2_1X_HEADER, *PBITMAPINFOOS2_1X_HEADER;
+
+typedef struct tagBITMAPFILEHEADER
+{
+ WORD bfType;
+ DWORD bfSize;
+ WORD bfReserved1;
+ WORD bfReserved2;
+ DWORD bfOffBits;
+} BITMAPFILEHEADER, *PBITMAPFILEHEADER;
+
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void SwapInfoHeader(BITMAPINFOHEADER *header)
+{
+ SwapLong(&header->biSize);
+ SwapLong((DWORD *)&header->biWidth);
+ SwapLong((DWORD *)&header->biHeight);
+ SwapShort(&header->biPlanes);
+ SwapShort(&header->biBitCount);
+ SwapLong(&header->biCompression);
+ SwapLong(&header->biSizeImage);
+ SwapLong((DWORD *)&header->biXPelsPerMeter);
+ SwapLong((DWORD *)&header->biYPelsPerMeter);
+ SwapLong(&header->biClrUsed);
+ SwapLong(&header->biClrImportant);
+}
+
+static void SwapCoreHeader(BITMAPCOREHEADER *header)
+{
+ SwapLong(&header->bcSize);
+ SwapShort(&header->bcWidth);
+ SwapShort(&header->bcHeight);
+ SwapShort(&header->bcPlanes);
+ SwapShort(&header->bcBitCnt);
+}
+
+static void SwapOS21XHeader(BITMAPINFOOS2_1X_HEADER *header)
+{
+ SwapLong(&header->biSize);
+ SwapShort(&header->biWidth);
+ SwapShort(&header->biHeight);
+ SwapShort(&header->biPlanes);
+ SwapShort(&header->biBitCount);
+}
+
+static void SwapFileHeader(BITMAPFILEHEADER *header)
+{
+ SwapShort(&header->bfType);
+ SwapLong(&header->bfSize);
+ SwapShort(&header->bfReserved1);
+ SwapShort(&header->bfReserved2);
+ SwapLong(&header->bfOffBits);
+}
+#endif
+
+// --------------------------------------------------------------------------
+
+/**
+Load uncompressed image pixels for 1-, 4-, 8-, 16-, 24- and 32-bit dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param dib Image to be loaded
+@param height Image height
+@param pitch Image pitch
+@param bit_count Image bit-depth (1-, 4-, 8-, 16-, 24- or 32-bit)
+*/
+static void LoadPixelData(FreeImageIO *io, fi_handle handle, FIBITMAP *dib, int height, int pitch,
+ int bit_count)
+{
+ (void)bit_count;
+ // Load pixel data
+ // NB: height can be < 0 for BMP data
+ if (height > 0) {
+ io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle);
+ } else {
+ int positiveHeight = abs(height);
+ for (int c = 0; c < positiveHeight; ++c) {
+ io->read_proc((void *)FreeImage_GetScanLine(dib, positiveHeight - c - 1), pitch, 1,
+ handle);
+ }
+ }
+
+// swap as needed
+#ifdef FREEIMAGE_BIGENDIAN
+ if (bit_count == 16) {
+ for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
+ for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ SwapShort(pixel);
+ pixel++;
+ }
+ }
+ }
+#endif
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ if (bit_count == 24 || bit_count == 32) {
+ for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *pixel = FreeImage_GetScanLine(dib, y);
+ for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ INPLACESWAP(pixel[0], pixel[2]);
+ pixel += (bit_count >> 3);
+ }
+ }
+ }
+#endif
+}
+
+/**
+Load image pixels for 4-bit RLE compressed dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param width Image width
+@param height Image height
+@param dib Image to be loaded
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL LoadPixelDataRLE4(FreeImageIO *io, fi_handle handle, int width, int height,
+ FIBITMAP *dib)
+{
+ int status_byte = 0;
+ BYTE second_byte = 0;
+ int bits = 0;
+
+ BYTE *pixels = NULL; // temporary 8-bit buffer
+
+ try {
+ height = abs(height);
+
+ pixels = (BYTE *)malloc(width * height * sizeof(BYTE));
+ if (!pixels)
+ throw(1);
+ memset(pixels, 0, width * height * sizeof(BYTE));
+
+ BYTE *q = pixels;
+ BYTE *end = pixels + height * width;
+
+ for (int scanline = 0; scanline < height;) {
+ if (q < pixels || q >= end) {
+ break;
+ }
+ if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ if (status_byte != 0) {
+ status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
+ // Encoded mode
+ if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ for (int i = 0; i < status_byte; i++) {
+ *q++ = (BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
+ }
+ bits += status_byte;
+ } else {
+ // Escape mode
+ if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ switch (status_byte) {
+ case RLE_ENDOFLINE: {
+ // End of line
+ bits = 0;
+ scanline++;
+ q = pixels + scanline * width;
+ } break;
+
+ case RLE_ENDOFBITMAP:
+ // End of bitmap
+ q = end;
+ break;
+
+ case RLE_DELTA: {
+ // read the delta values
+
+ BYTE delta_x = 0;
+ BYTE delta_y = 0;
+
+ if (io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ if (io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+
+ // apply them
+
+ bits += delta_x;
+ scanline += delta_y;
+ q = pixels + scanline * width + bits;
+ } break;
+
+ default: {
+ // Absolute mode
+ status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
+ for (int i = 0; i < status_byte; i++) {
+ if ((i & 0x01) == 0) {
+ if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ }
+ *q++ =
+ (BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
+ }
+ bits += status_byte;
+ // Read pad byte
+ if (((status_byte & 0x03) == 1) || ((status_byte & 0x03) == 2)) {
+ BYTE padding = 0;
+ if (io->read_proc(&padding, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ }
+ } break;
+ }
+ }
+ }
+
+ {
+ // Convert to 4-bit
+ for (int y = 0; y < height; y++) {
+ const BYTE *src = (BYTE *)pixels + y * width;
+ BYTE *dst = FreeImage_GetScanLine(dib, y);
+
+ BOOL hinibble = TRUE;
+
+ for (int cols = 0; cols < width; cols++) {
+ if (hinibble) {
+ dst[cols >> 1] = (src[cols] << 4);
+ } else {
+ dst[cols >> 1] |= src[cols];
+ }
+
+ hinibble = !hinibble;
+ }
+ }
+ }
+
+ free(pixels);
+
+ return TRUE;
+
+ } catch (int) {
+ if (pixels)
+ free(pixels);
+ return FALSE;
+ }
+}
+
+/**
+Load image pixels for 8-bit RLE compressed dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param width Image width
+@param height Image height
+@param dib Image to be loaded
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL LoadPixelDataRLE8(FreeImageIO *io, fi_handle handle, int width, int height,
+ FIBITMAP *dib)
+{
+ BYTE status_byte = 0;
+ BYTE second_byte = 0;
+ int scanline = 0;
+ int bits = 0;
+
+ for (;;) {
+ if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ switch (status_byte) {
+ case RLE_COMMAND:
+ if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ switch (status_byte) {
+ case RLE_ENDOFLINE:
+ bits = 0;
+ scanline++;
+ break;
+
+ case RLE_ENDOFBITMAP:
+ return TRUE;
+
+ case RLE_DELTA: {
+ // read the delta values
+
+ BYTE delta_x = 0;
+ BYTE delta_y = 0;
+
+ if (io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+ if (io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ // apply them
+
+ bits += delta_x;
+ scanline += delta_y;
+
+ break;
+ }
+
+ default: {
+ if (scanline >= abs(height)) {
+ return TRUE;
+ }
+
+ int count = MIN((int)status_byte, width - bits);
+
+ BYTE *sline = FreeImage_GetScanLine(dib, scanline);
+
+ if (io->read_proc((void *)(sline + bits), sizeof(BYTE) * count, 1, handle) != 1) {
+ return FALSE;
+ }
+
+ // align run length to even number of bytes
+
+ if ((status_byte & 1) == 1) {
+ if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+ }
+
+ bits += status_byte;
+
+ break;
+ }
+ }
+
+ break;
+
+ default: {
+ if (scanline >= abs(height)) {
+ return TRUE;
+ }
+
+ int count = MIN((int)status_byte, width - bits);
+
+ BYTE *sline = FreeImage_GetScanLine(dib, scanline);
+
+ if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ for (int i = 0; i < count; i++) {
+ *(sline + bits) = second_byte;
+
+ bits++;
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags,
+ unsigned bitmap_bits_offset)
+{
+ FIBITMAP *dib = NULL;
+ (void)flags;
+ try {
+ // load the info header
+
+ BITMAPINFOHEADER bih;
+
+ io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(&bih);
+#endif
+
+ // keep some general information about the bitmap
+
+ int used_colors = bih.biClrUsed;
+ int width = bih.biWidth;
+ int height = bih.biHeight; // WARNING: height can be < 0 => check each call using 'height'
+ // as a parameter
+ int alloc_height = abs(height);
+ int bit_count = bih.biBitCount;
+ int compression = bih.biCompression;
+ int pitch = CalculatePitch(CalculateLine(width, bit_count));
+
+ switch (bit_count) {
+ case 1:
+ case 4:
+ case 8: {
+ if ((used_colors <= 0) || (used_colors > CalculateUsedPaletteEntries(bit_count)))
+ used_colors = CalculateUsedPaletteEntries(bit_count);
+
+ // allocate enough memory to hold the bitmap (header, palette, pixels) and read the
+ // palette
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, io);
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // load the palette
+
+ io->read_proc(FreeImage_GetPalette(dib), used_colors * sizeof(RGBQUAD), 1, handle);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ for (int i = 0; i < used_colors; i++) {
+ INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
+ }
+#endif
+
+ // seek to the actual pixel data.
+ // this is needed because sometimes the palette is larger than the entries it contains
+ // predicts
+
+ if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ + (used_colors * sizeof(RGBQUAD))))
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read the pixel data
+
+ switch (compression) {
+ case BI_RGB:
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+ return dib;
+
+ case BI_RLE4:
+ if (LoadPixelDataRLE4(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE4 BMP data";
+ }
+ break;
+
+ case BI_RLE8:
+ if (LoadPixelDataRLE8(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE8 BMP data";
+ }
+ break;
+
+ default:
+ throw "compression type not supported";
+ }
+ } break; // 1-, 4-, 8-bit
+
+ case 16: {
+ if (bih.biCompression == BI_BITFIELDS) {
+ DWORD bitfields[3];
+
+ io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, bitfields[0], bitfields[1],
+ bitfields[2], io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI16_555_RED_MASK,
+ FI16_555_GREEN_MASK, FI16_555_BLUE_MASK, io);
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ } break; // 16-bit
+
+ case 24:
+ case 32: {
+ if (bih.biCompression == BI_BITFIELDS) {
+ DWORD bitfields[3];
+
+ io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, bitfields[0], bitfields[1],
+ bitfields[2], io);
+ } else {
+ if (bit_count == 32) {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ }
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ if (used_colors > 0) {
+ io->seek_proc(handle, used_colors * sizeof(RGBQUAD), SEEK_CUR);
+ } else if ((bih.biCompression != BI_BITFIELDS)
+ && (bitmap_bits_offset
+ > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) {
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+ }
+
+ // read in the bitmap bits
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ // check if the bitmap contains transparency, if so enable it in the header
+
+ return dib;
+ } break; // 24-, 32-bit
+ }
+ } catch (const char *message) {
+ if (dib) {
+ FreeImage_Unload(dib);
+ }
+ if (message) {
+ FreeImage_OutputMessageProc(s_format_id, message, io);
+ }
+ }
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags,
+ unsigned bitmap_bits_offset)
+{
+ FIBITMAP *dib = NULL;
+ (void)flags;
+ try {
+ // load the info header
+
+ BITMAPINFOHEADER bih;
+
+ io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(&bih);
+#endif
+
+ // keep some general information about the bitmap
+
+ int used_colors = bih.biClrUsed;
+ int width = bih.biWidth;
+ int height = bih.biHeight; // WARNING: height can be < 0 => check each read_proc using
+ // 'height' as a parameter
+ int alloc_height = abs(height);
+ int bit_count = bih.biBitCount;
+ int compression = bih.biCompression;
+ int pitch = CalculatePitch(CalculateLine(width, bit_count));
+
+ switch (bit_count) {
+ case 1:
+ case 4:
+ case 8: {
+ if ((used_colors <= 0) || (used_colors > CalculateUsedPaletteEntries(bit_count)))
+ used_colors = CalculateUsedPaletteEntries(bit_count);
+
+ // allocate enough memory to hold the bitmap (header, palette, pixels) and read the
+ // palette
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, io);
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // load the palette
+
+ io->seek_proc(handle, sizeof(BITMAPFILEHEADER) + bih.biSize, SEEK_SET);
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ for (int count = 0; count < used_colors; count++) {
+ FILE_BGR bgr;
+
+ io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
+
+ pal[count].rgbRed = bgr.r;
+ pal[count].rgbGreen = bgr.g;
+ pal[count].rgbBlue = bgr.b;
+ }
+
+ // seek to the actual pixel data.
+ // this is needed because sometimes the palette is larger than the entries it contains
+ // predicts
+
+ if (bitmap_bits_offset
+ > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3)))
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read the pixel data
+
+ switch (compression) {
+ case BI_RGB:
+ // load pixel data
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+ return dib;
+
+ case BI_RLE4:
+ if (LoadPixelDataRLE4(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE4 BMP data";
+ }
+ break;
+
+ case BI_RLE8:
+ if (LoadPixelDataRLE8(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE8 BMP data";
+ }
+ break;
+
+ default:
+ throw "compression type not supported";
+ }
+ }
+
+ case 16: {
+ if (bih.biCompression == 3) {
+ DWORD bitfields[3];
+
+ io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, bitfields[0], bitfields[1],
+ bitfields[2], io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI16_555_RED_MASK,
+ FI16_555_GREEN_MASK, FI16_555_BLUE_MASK, io);
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ if (bitmap_bits_offset
+ > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+ }
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+
+ case 24:
+ case 32: {
+ if (bit_count == 32) {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ if (bitmap_bits_offset
+ > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3)))
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read in the bitmap bits
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+ }
+ } catch (const char *message) {
+ if (dib)
+ FreeImage_Unload(dib);
+
+ FreeImage_OutputMessageProc(s_format_id, message, io);
+ }
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags,
+ unsigned bitmap_bits_offset)
+{
+ FIBITMAP *dib = NULL;
+ (void)flags;
+ try {
+ BITMAPINFOOS2_1X_HEADER bios2_1x;
+
+ io->read_proc(&bios2_1x, sizeof(BITMAPINFOOS2_1X_HEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapOS21XHeader(&bios2_1x);
+#endif
+ // keep some general information about the bitmap
+
+ int used_colors = 0;
+ int width = bios2_1x.biWidth;
+ int height = bios2_1x.biHeight; // WARNING: height can be < 0 => check each read_proc using
+ // 'height' as a parameter
+ int alloc_height = abs(height);
+ int bit_count = bios2_1x.biBitCount;
+ int pitch = CalculatePitch(CalculateLine(width, bit_count));
+
+ switch (bit_count) {
+ case 1:
+ case 4:
+ case 8: {
+ used_colors = CalculateUsedPaletteEntries(bit_count);
+
+ // allocate enough memory to hold the bitmap (header, palette, pixels) and read the
+ // palette
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, io);
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information to default values (72 dpi in english units)
+ FreeImage_SetDotsPerMeterX(dib, 2835);
+ FreeImage_SetDotsPerMeterY(dib, 2835);
+
+ // load the palette
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ for (int count = 0; count < used_colors; count++) {
+ FILE_BGR bgr;
+
+ io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
+
+ pal[count].rgbRed = bgr.r;
+ pal[count].rgbGreen = bgr.g;
+ pal[count].rgbBlue = bgr.b;
+ }
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read the pixel data
+
+ // load pixel data
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+
+ case 16: {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI16_555_RED_MASK,
+ FI16_555_GREEN_MASK, FI16_555_BLUE_MASK, io);
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information to default values (72 dpi in english units)
+ FreeImage_SetDotsPerMeterX(dib, 2835);
+ FreeImage_SetDotsPerMeterY(dib, 2835);
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+
+ case 24:
+ case 32: {
+ if (bit_count == 32) {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information to default values (72 dpi in english units)
+ FreeImage_SetDotsPerMeterX(dib, 2835);
+ FreeImage_SetDotsPerMeterY(dib, 2835);
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ // check if the bitmap contains transparency, if so enable it in the header
+
+ return dib;
+ }
+ }
+ } catch (const char *message) {
+ if (dib)
+ FreeImage_Unload(dib);
+
+ FreeImage_OutputMessageProc(s_format_id, message, io);
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+// ----------------------------------------------------------
+
+static FIBITMAP *DoLoadBMP(FreeImageIO *io, fi_handle handle, int flags)
+{
+ if (handle != NULL) {
+ BITMAPFILEHEADER bitmapfileheader;
+ DWORD type = 0;
+ BYTE magic[2];
+
+ // we use this offset value to make seemingly absolute seeks relative in the file
+
+ long offset_in_file = io->tell_proc(handle);
+
+ // read the magic
+
+ io->read_proc(&magic, sizeof(magic), 1, handle);
+
+ // compare the magic with the number we know
+
+ // somebody put a comment here explaining the purpose of this loop
+ while (memcmp(&magic, "BA", 2) == 0) {
+ io->read_proc(&bitmapfileheader.bfSize, sizeof(DWORD), 1, handle);
+ io->read_proc(&bitmapfileheader.bfReserved1, sizeof(WORD), 1, handle);
+ io->read_proc(&bitmapfileheader.bfReserved2, sizeof(WORD), 1, handle);
+ io->read_proc(&bitmapfileheader.bfOffBits, sizeof(DWORD), 1, handle);
+ io->read_proc(&magic, sizeof(magic), 1, handle);
+ }
+
+ // read the fileheader
+
+ io->seek_proc(handle, (0 - (int)sizeof(magic)), SEEK_CUR);
+ io->read_proc(&bitmapfileheader, (int)sizeof(BITMAPFILEHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapFileHeader(&bitmapfileheader);
+#endif
+
+ // read the first byte of the infoheader
+
+ io->read_proc(&type, sizeof(DWORD), 1, handle);
+ io->seek_proc(handle, 0 - (int)sizeof(DWORD), SEEK_CUR);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapLong(&type);
+#endif
+
+ // call the appropriate load function for the found bitmap type
+
+ if (type == 40)
+ return LoadWindowsBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+
+ if (type == 12)
+ return LoadOS21XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+
+ if (type <= 64)
+ return LoadOS22XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+
+ char buf[256];
+ sprintf(buf, "unknown bmp subtype with id %d", type);
+ FreeImage_OutputMessageProc(s_format_id, buf, io);
+ }
+
+ return NULL;
+}
+
+template <QT3DSU32 TBitWidth>
+struct SPaletteIndexer
+{
+ static inline uint32_t IndexOf(const uint8_t *inData, uint32_t inPos)
+ {
+ uint32_t divisor = 8 / TBitWidth;
+ uint32_t byte = inPos / divisor;
+ uint32_t modulus = inPos % divisor;
+ uint32_t shift = TBitWidth * modulus;
+ uint32_t mask = (1 << TBitWidth) - 1;
+ mask = mask << shift;
+ uint32_t byteData = inData[byte];
+ return (byteData & mask) >> shift;
+ }
+};
+
+template <>
+struct SPaletteIndexer<1>
+{
+ static inline uint32_t IndexOf(const uint8_t *inData, uint32_t inPos)
+ {
+ uint32_t byte = (inPos / 8);
+ uint32_t bit = 1 << (7 - (inPos % 8));
+ uint32_t byteData = inData[byte];
+ return (byteData & bit) ? 1 : 0;
+ }
+};
+
+template <>
+struct SPaletteIndexer<8>
+{
+ static inline uint32_t IndexOf(const uint8_t *inData, uint32_t inPos)
+ {
+ uint32_t byte = inPos;
+ uint32_t bit = 0xFF;
+ uint32_t byteData = inData[byte];
+ return byteData & bit;
+ }
+};
+
+static inline void assignQuad(uint8_t *dest, const RGBQUAD &quad)
+{
+ dest[0] = quad.rgbRed;
+ dest[1] = quad.rgbGreen;
+ dest[2] = quad.rgbBlue;
+}
+
+template <QT3DSU32 bitCount>
+inline void LoadPalettized(bool inFlipY, const RGBQUAD *palette, void *data, uint8_t *newData,
+ int width, int height, int components, int transparentIndex)
+{
+ const uint8_t *oldData = (const uint8_t *)data;
+ int pitch = CalculatePitch(CalculateLine(width, bitCount));
+ for (uint32_t h = 0; h < (uint32_t)height; ++h) {
+ uint32_t relHeight = h;
+ if (inFlipY)
+ relHeight = ((uint32_t)height) - h - 1;
+ for (uint32_t w = 0; w < (uint32_t)width; ++w) {
+ const uint8_t *dataLine = oldData + pitch * h;
+ uint32_t pos = width * relHeight + w;
+ uint32_t paletteIndex = SPaletteIndexer<bitCount>::IndexOf(dataLine, w);
+ const RGBQUAD &theQuad = palette[paletteIndex];
+ uint8_t *writePtr = newData + (pos * components);
+ assignQuad(writePtr, theQuad);
+ if (paletteIndex == (uint32_t)transparentIndex && components == 4) {
+ writePtr[3] = 0;
+ }
+ }
+ }
+}
+
+inline int firstHighBit(int data)
+{
+ if (data == 0)
+ return 0;
+ int idx = 0;
+ while ((data % 2) == 0) {
+ data = data >> 1;
+ ++idx;
+ }
+ return idx;
+}
+
+struct SMaskData
+{
+ uint32_t mask;
+ uint32_t shift;
+ uint32_t max;
+
+ SMaskData(int inMask)
+ {
+ mask = inMask;
+ shift = firstHighBit(mask);
+ max = mask >> shift;
+ }
+
+ inline uint8_t MapColor(uint32_t color) const
+ {
+ uint32_t intermediateValue = (color & mask) >> shift;
+ return (uint8_t)((intermediateValue * 255) / max);
+ }
+};
+
+template <QT3DSU32>
+struct ColorAccess
+{
+};
+
+template <>
+struct ColorAccess<16>
+{
+ static uint32_t GetPixelWidth() { return 2; }
+ static uint32_t GetColor(const char8_t *src)
+ {
+ return (uint32_t) * reinterpret_cast<const QT3DSU16 *>(src);
+ }
+};
+
+template <>
+struct ColorAccess<24>
+{
+ static uint32_t GetPixelWidth() { return 3; }
+ static uint32_t GetColor(const char8_t *src)
+ {
+ return (uint32_t)(*reinterpret_cast<const QT3DSU32 *>(src) & 0xFFFFFF);
+ }
+};
+
+template <>
+struct ColorAccess<32>
+{
+ static uint32_t GetPixelWidth() { return 4; }
+ static uint32_t GetColor(const char8_t *src)
+ {
+ return *reinterpret_cast<const uint32_t *>(src);
+ }
+};
+
+template <QT3DSU32 TBitCount>
+inline void LoadMasked(bool inFlipY, QT3DSI32 *inMasks, void *data, uint8_t *newData, int width,
+ int height)
+{
+ const char8_t *oldData = (const char8_t *)data;
+ SMaskData rMask(inMasks[0]);
+ SMaskData gMask(inMasks[1]);
+ SMaskData bMask(inMasks[2]);
+ for (int h = 0; h < height; ++h) {
+ int relHeight = h;
+ if (inFlipY)
+ relHeight = height - h - 1;
+ for (int w = 0; w < width; ++w) {
+ int pos = width * relHeight + w;
+ const char8_t *readPtr = oldData + (pos * ColorAccess<TBitCount>::GetPixelWidth());
+ uint8_t *writePtr = newData + (pos * 3);
+ uint32_t colorVal = ColorAccess<TBitCount>::GetColor(readPtr);
+ writePtr[0] = rMask.MapColor(colorVal);
+ writePtr[1] = gMask.MapColor(colorVal);
+ writePtr[2] = bMask.MapColor(colorVal);
+ }
+ }
+}
+
+void SLoadedTexture::FreeImagePostProcess(bool inFlipY)
+{
+ // We always convert 32 bit RGBA
+ if (m_ExtendedFormat != ExtendedTextureFormats::NoExtendedFormat) {
+
+ QT3DSU32 stride = 3 * width;
+ format = NVRenderTextureFormats::RGB8;
+ components = 3;
+ if (m_ExtendedFormat == ExtendedTextureFormats::Palettized && m_TransparentPaletteIndex > -1
+ && m_TransparentPaletteIndex < 256) {
+ stride = 4 * width;
+ components = 4;
+ format = NVRenderTextureFormats::RGBA8;
+ }
+ QT3DSU32 byteSize = height * stride;
+ uint8_t *newData =
+ (uint8_t *)m_Allocator.allocate(byteSize, "texture data", __FILE__, __LINE__);
+ if (format == NVRenderTextureFormats::RGBA8)
+ memSet(newData, 255, byteSize);
+ switch (m_ExtendedFormat) {
+ case ExtendedTextureFormats::Palettized: {
+ RGBQUAD *palette = (RGBQUAD *)m_Palette;
+ switch (m_BitCount) {
+ case 1:
+ LoadPalettized<1>(inFlipY, palette, data, newData, width, height, components,
+ m_TransparentPaletteIndex);
+ break;
+ case 2:
+ LoadPalettized<2>(inFlipY, palette, data, newData, width, height, components,
+ m_TransparentPaletteIndex);
+ break;
+ case 4:
+ LoadPalettized<4>(inFlipY, palette, data, newData, width, height, components,
+ m_TransparentPaletteIndex);
+ break;
+ case 8:
+ LoadPalettized<8>(inFlipY, palette, data, newData, width, height, components,
+ m_TransparentPaletteIndex);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ memSet(newData, 0, byteSize);
+ break;
+ }
+ } break;
+ case ExtendedTextureFormats::CustomRGB: {
+ switch (m_BitCount) {
+ case 16:
+ LoadMasked<16>(inFlipY, m_CustomMasks, data, newData, width, height);
+ break;
+ case 24:
+ LoadMasked<24>(inFlipY, m_CustomMasks, data, newData, width, height);
+ break;
+ case 32:
+ LoadMasked<32>(inFlipY, m_CustomMasks, data, newData, width, height);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ memSet(newData, 0, byteSize);
+ break;
+ }
+ } break;
+ default:
+ QT3DS_ASSERT(false);
+ memSet(newData, 0, byteSize);
+ break;
+ }
+ m_Allocator.deallocate(data);
+ if (m_Palette)
+ m_Allocator.deallocate(m_Palette);
+ data = newData;
+ m_Palette = NULL;
+ m_BitCount = 0;
+ this->dataSizeInBytes = byteSize;
+ m_ExtendedFormat = ExtendedTextureFormats::NoExtendedFormat;
+ }
+}
+
+SLoadedTexture *SLoadedTexture::LoadBMP(ISeekableIOStream &inStream, bool inFlipY,
+ NVFoundationBase &inFnd,
+ qt3ds::render::NVRenderContextType renderContextType)
+{
+ Q_UNUSED(renderContextType)
+ FreeImageIO theIO(inFnd.getAllocator(), inFnd);
+ SLoadedTexture *retval = DoLoadBMP(&theIO, &inStream, 0);
+ if (retval)
+ retval->FreeImagePostProcess(inFlipY);
+ return retval;
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp
new file mode 100644
index 0000000..19d41d1
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp
@@ -0,0 +1,695 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSRenderLoadedTextureDDS.h"
+#include "Qt3DSRenderLoadedTextureFreeImageCompat.h"
+
+using namespace qt3ds::render;
+
+namespace qt3ds {
+namespace render {
+
+ static int s_exception_string;
+
+ //================================================================================
+ // DXT data-layout structure definitions.
+ typedef struct
+ {
+ QT3DSU16 col0; // 16-bit 565 interpolant endpoints
+ QT3DSU16 col1;
+ QT3DSU8 row[4]; // 4x4 * 2bpp color-index == 4 bytes.
+ } DXTColBlock;
+
+ typedef struct
+ {
+ QT3DSU16 row[4]; // 4x4 * 4bpp alpha == 8 bytes. (pure 4-bit alpha values)
+ } DXT3AlphaBlock;
+
+ typedef struct
+ {
+ QT3DSU8 alpha0; // 8-bit alpha interpolant endpoints
+ QT3DSU8 alpha1;
+ QT3DSU8 row[6]; // 4x4 * 3bpp alpha-index == 48bits == 6 bytes.
+ } DXT5AlphaBlock;
+
+ typedef struct
+ {
+ QT3DSU8 red;
+ QT3DSU8 green;
+ QT3DSU8 blue;
+ QT3DSU8 alpha;
+ } Color8888;
+
+//================================================================================
+// Various DDS file defines
+
+#define DDSD_CAPS 0x00000001l
+#define DDSD_HEIGHT 0x00000002l
+#define DDSD_WIDTH 0x00000004l
+#define DDSD_PIXELFORMAT 0x00001000l
+#define DDS_ALPHAPIXELS 0x00000001l
+#define DDS_FOURCC 0x00000004l
+#define DDS_PITCH 0x00000008l
+#define DDS_COMPLEX 0x00000008l
+#define DDS_RGB 0x00000040l
+#define DDS_TEXTURE 0x00001000l
+#define DDS_MIPMAPCOUNT 0x00020000l
+#define DDS_LINEARSIZE 0x00080000l
+#define DDS_VOLUME 0x00200000l
+#define DDS_MIPMAP 0x00400000l
+#define DDS_DEPTH 0x00800000l
+
+#define DDS_CUBEMAP 0x00000200L
+#define DDS_CUBEMAP_POSITIVEX 0x00000400L
+#define DDS_CUBEMAP_NEGATIVEX 0x00000800L
+#define DDS_CUBEMAP_POSITIVEY 0x00001000L
+#define DDS_CUBEMAP_NEGATIVEY 0x00002000L
+#define DDS_CUBEMAP_POSITIVEZ 0x00004000L
+#define DDS_CUBEMAP_NEGATIVEZ 0x00008000L
+
+#define FOURCC_DXT1 0x31545844 //(MAKEFOURCC('D','X','T','1'))
+#define FOURCC_DXT3 0x33545844 //(MAKEFOURCC('D','X','T','3'))
+#define FOURCC_DXT5 0x35545844 //(MAKEFOURCC('D','X','T','5'))
+
+#define DDS_MAGIC_FLIPPED 0x0F7166ED
+
+ //================================================================================
+ // DDS file format structures.
+ typedef struct _DDS_PIXELFORMAT
+ {
+ QT3DSU32 dwSize;
+ QT3DSU32 dwFlags;
+ QT3DSU32 dwFourCC;
+ QT3DSU32 dwRGBBitCount;
+ QT3DSU32 dwRBitMask;
+ QT3DSU32 dwGBitMask;
+ QT3DSU32 dwBBitMask;
+ QT3DSU32 dwABitMask;
+ } DDS_PIXELFORMAT;
+
+ typedef struct _DDS_HEADER
+ {
+ QT3DSU32 dwSize;
+ QT3DSU32 dwFlags;
+ QT3DSU32 dwHeight;
+ QT3DSU32 dwWidth;
+ QT3DSU32 dwPitchOrLinearSize;
+ QT3DSU32 dwDepth;
+ QT3DSU32 dwMipMapCount;
+ QT3DSU32 dwReserved1[11];
+ DDS_PIXELFORMAT ddspf;
+ QT3DSU32 dwCaps1;
+ QT3DSU32 dwCaps2;
+ QT3DSU32 dwReserved2[3];
+ } DDS_HEADER;
+
+ //================================================================================
+ // helper functions
+ //================================================================================
+
+ // helper macros.
+ static inline void NvSwapChar(QT3DSU8 &a, QT3DSU8 &b)
+ {
+ QT3DSU8 tmp;
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ static inline void NvSwapShort(QT3DSU16 &a, QT3DSU16 &b)
+ {
+ QT3DSU16 tmp;
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ //================================================================================
+ //================================================================================
+ static void flip_blocks_dxtc1(DXTColBlock *line, QT3DSI32 numBlocks)
+ {
+ DXTColBlock *curblock = line;
+ QT3DSI32 i;
+
+ for (i = 0; i < numBlocks; i++) {
+ NvSwapChar(curblock->row[0], curblock->row[3]);
+ NvSwapChar(curblock->row[1], curblock->row[2]);
+ curblock++;
+ }
+ }
+
+ //================================================================================
+ //================================================================================
+ static void flip_blocks_dxtc3(DXTColBlock *line, QT3DSI32 numBlocks)
+ {
+ DXTColBlock *curblock = line;
+ DXT3AlphaBlock *alphablock;
+ QT3DSI32 i;
+
+ for (i = 0; i < numBlocks; i++) {
+ alphablock = (DXT3AlphaBlock *)curblock;
+
+ NvSwapShort(alphablock->row[0], alphablock->row[3]);
+ NvSwapShort(alphablock->row[1], alphablock->row[2]);
+ curblock++;
+
+ NvSwapChar(curblock->row[0], curblock->row[3]);
+ NvSwapChar(curblock->row[1], curblock->row[2]);
+ curblock++;
+ }
+ }
+
+ static void flip_dxt5_alpha(DXT5AlphaBlock *block)
+ {
+ QT3DSI8 gBits[4][4];
+
+ const QT3DSU32 mask = 0x00000007; // bits = 00 00 01 11
+ QT3DSU32 bits = 0;
+ memcpy(&bits, &block->row[0], sizeof(QT3DSI8) * 3);
+
+ gBits[0][0] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[0][1] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[0][2] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[0][3] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[1][0] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[1][1] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[1][2] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[1][3] = (QT3DSI8)(bits & mask);
+
+ bits = 0;
+ memcpy(&bits, &block->row[3], sizeof(QT3DSI8) * 3);
+
+ gBits[2][0] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[2][1] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[2][2] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[2][3] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[3][0] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[3][1] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[3][2] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[3][3] = (QT3DSI8)(bits & mask);
+
+ bits = (gBits[3][0] << 0) | (gBits[3][1] << 3) | (gBits[3][2] << 6) | (gBits[3][3] << 9)
+ | (gBits[2][0] << 12) | (gBits[2][1] << 15) | (gBits[2][2] << 18) | (gBits[2][3] << 21);
+ memcpy(&block->row[0], &bits, 3);
+
+ bits = (gBits[1][0] << 0) | (gBits[1][1] << 3) | (gBits[1][2] << 6) | (gBits[1][3] << 9)
+ | (gBits[0][0] << 12) | (gBits[0][1] << 15) | (gBits[0][2] << 18) | (gBits[0][3] << 21);
+ memcpy(&block->row[3], &bits, 3);
+ }
+
+ static void flip_blocks_dxtc5(DXTColBlock *line, QT3DSI32 numBlocks)
+ {
+ DXTColBlock *curblock = line;
+ DXT5AlphaBlock *alphablock;
+ QT3DSI32 i;
+
+ for (i = 0; i < numBlocks; i++) {
+ alphablock = (DXT5AlphaBlock *)curblock;
+
+ flip_dxt5_alpha(alphablock);
+ curblock++;
+
+ NvSwapChar(curblock->row[0], curblock->row[3]);
+ NvSwapChar(curblock->row[1], curblock->row[2]);
+ curblock++;
+ }
+ }
+
+ static void flip_data_vertical(FreeImageIO *io, QT3DSI8 *image, QT3DSI32 width, QT3DSI32 height,
+ Qt3DSDDSImage *info)
+ {
+ if (info->compressed) {
+ QT3DSI32 linesize, j;
+ DXTColBlock *top;
+ DXTColBlock *bottom;
+ QT3DSI8 *tmp;
+ void (*flipblocks)(DXTColBlock *, QT3DSI32) = NULL;
+ QT3DSI32 xblocks = width / 4;
+ QT3DSI32 yblocks = height / 4;
+ QT3DSI32 blocksize;
+
+ switch (info->format) {
+ case qt3ds::render::NVRenderTextureFormats::RGBA_DXT1:
+ blocksize = 8;
+ flipblocks = &flip_blocks_dxtc1;
+ break;
+ case qt3ds::render::NVRenderTextureFormats::RGBA_DXT3:
+ blocksize = 16;
+ flipblocks = &flip_blocks_dxtc3;
+ break;
+ case qt3ds::render::NVRenderTextureFormats::RGBA_DXT5:
+ blocksize = 16;
+ flipblocks = &flip_blocks_dxtc5;
+ break;
+ default:
+ return;
+ }
+
+ linesize = xblocks * blocksize;
+ tmp = (QT3DSI8 *)QT3DS_ALLOC(io->m_Allocator, linesize, "flip_data_vertical compressed");
+
+ for (j = 0; j < (yblocks >> 1); j++) {
+ top = (DXTColBlock *)(void *)(image + j * linesize);
+ bottom = (DXTColBlock *)(void *)(image + (((yblocks - j) - 1) * linesize));
+
+ (*flipblocks)(top, xblocks);
+ (*flipblocks)(bottom, xblocks);
+
+ memcpy(tmp, bottom, linesize);
+ memcpy(bottom, top, linesize);
+ memcpy(top, tmp, linesize);
+ }
+
+ // Catch the middle row of blocks if there is one
+ // The loop above will skip the middle row
+ if (yblocks & 0x01) {
+ DXTColBlock *middle = (DXTColBlock *)(void *)(image + (yblocks >> 1) * linesize);
+ (*flipblocks)(middle, xblocks);
+ }
+
+ QT3DS_FREE(io->m_Allocator, tmp);
+ } else {
+ QT3DSI32 linesize = width * info->bytesPerPixel;
+ QT3DSI32 j;
+ QT3DSI8 *top;
+ QT3DSI8 *bottom;
+ QT3DSI8 *tmp;
+
+ // much simpler - just compute the line length and swap each row
+ tmp = (QT3DSI8 *)QT3DS_ALLOC(io->m_Allocator, linesize, "flip_data_vertical");
+ ;
+
+ for (j = 0; j < (height >> 1); j++) {
+ top = (QT3DSI8 *)(image + j * linesize);
+ bottom = (QT3DSI8 *)(image + (((height - j) - 1) * linesize));
+
+ memcpy(tmp, bottom, linesize);
+ memcpy(bottom, top, linesize);
+ memcpy(top, tmp, linesize);
+ }
+
+ QT3DS_FREE(io->m_Allocator, tmp);
+ }
+ }
+
+ static QT3DSI32 size_image(QT3DSI32 width, QT3DSI32 height, const Qt3DSDDSImage *image)
+ {
+ if (image->compressed) {
+ return ((width + 3) / 4) * ((height + 3) / 4)
+ * (image->format == qt3ds::render::NVRenderTextureFormats::RGBA_DXT1 ? 8 : 16);
+ } else {
+ return width * height * image->bytesPerPixel;
+ }
+ }
+
+ static QT3DSI32 total_image_data_size(Qt3DSDDSImage *image)
+ {
+ QT3DSI32 i, j, index = 0, size = 0, w, h;
+ QT3DSI32 cubeCount = image->cubemap ? 6 : 1;
+
+ for (j = 0; j < cubeCount; j++) {
+ w = image->width;
+ h = image->height;
+
+ for (i = 0; i < image->numMipmaps; i++) // account for base plus each mip
+ {
+ image->size[index] = size_image(w, h, image);
+ image->mipwidth[index] = w;
+ image->mipheight[index] = h;
+ size += image->size[index];
+ if (w != 1) {
+ w >>= 1;
+ }
+ if (h != 1) {
+ h >>= 1;
+ }
+
+ index++;
+ }
+ }
+
+ return (size);
+ }
+
+ void *Qt3DSDDSAllocDataBlock(FreeImageIO *io, Qt3DSDDSImage *image)
+ {
+ if (image) {
+ QT3DSI32 i;
+ QT3DSI32 size = total_image_data_size(image);
+ image->dataBlock =
+ QT3DS_ALLOC(io->m_Allocator, size,
+ "Qt3DSDDSAllocDataBlock"); // no need to calloc, as we fill every bit...
+ if (image->dataBlock == NULL) {
+ return NULL;
+ }
+
+ image->data[0] = image->dataBlock;
+
+ QT3DSI32 planes = image->numMipmaps * (image->cubemap ? 6 : 1);
+
+ for (i = 1; i < planes; i++) // account for base plus each mip
+ {
+ image->data[i] =
+ (void *)(((size_t)(image->data[i - 1])) + (size_t)image->size[i - 1]);
+ }
+
+ return (image->dataBlock); // in case caller wants to sanity check...
+ }
+ return (NULL);
+ }
+
+ static FIBITMAP *DoLoadDDS(FreeImageIO *io, IInStream &inStream, QT3DSI32 flipVertical)
+ {
+ FIBITMAP *dib = NULL;
+ DDS_HEADER ddsh;
+ QT3DSI8 filecode[4];
+ Qt3DSDDSImage *image = NULL;
+ bool needsBGRASwap = false;
+ ;
+ bool isAllreadyFlipped = false;
+
+ try {
+ // check file code
+ inStream.Read(filecode, 4);
+ if (memcmp(filecode, "DDS ", 4)) {
+ throw "Invalid DDS file";
+ }
+
+ image = (Qt3DSDDSImage *)QT3DS_ALLOC(io->m_Allocator, sizeof(Qt3DSDDSImage), "DoLoadDDS");
+ if (image == NULL) {
+ throw "Qt3DSDDSImage allocation failed";
+ }
+ memset(image, 0, sizeof(Qt3DSDDSImage));
+
+ // read in DDS header
+ inStream.Read(&ddsh, 1);
+
+ // check if image is a cubempap
+ if (ddsh.dwCaps2 & DDS_CUBEMAP) {
+ const QT3DSI32 allFaces = DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_POSITIVEY
+ | DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEX | DDS_CUBEMAP_NEGATIVEY
+ | DDS_CUBEMAP_NEGATIVEZ;
+
+ if ((ddsh.dwCaps2 & allFaces) != allFaces) {
+ throw "Not all cubemap faces defined - not supported";
+ }
+
+ image->cubemap = 1;
+ } else {
+ image->cubemap = 0;
+ }
+
+ // check if image is a volume texture
+ if ((ddsh.dwCaps2 & DDS_VOLUME) && (ddsh.dwDepth > 0)) {
+ throw "Volume textures not supported";
+ }
+
+ // allocated the memory for the structure we return
+ dib = QT3DS_NEW(io->m_Allocator, SLoadedTexture)(io->m_Allocator);
+ if (dib == NULL) {
+ throw "DIB allocation failed";
+ }
+
+ // figure out what the image format is
+ if (ddsh.ddspf.dwFlags & DDS_FOURCC) {
+ switch (ddsh.ddspf.dwFourCC) {
+ case FOURCC_DXT1:
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT1;
+ image->components = 3;
+ image->compressed = 1;
+ image->alpha = 0; // Ugh - for backwards compatibility
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGB_DXT1;
+ break;
+ case FOURCC_DXT3:
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT3;
+ image->components = 4;
+ image->compressed = 1;
+ image->alpha = 1;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT3;
+ break;
+ case FOURCC_DXT5:
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT5;
+ image->components = 4;
+ image->compressed = 1;
+ image->alpha = 1;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT5;
+ break;
+ default:
+ throw "Unsupported FOURCC code";
+ }
+ } else {
+ // Check for a supported pixel format
+ if ((ddsh.ddspf.dwRGBBitCount == 32) && (ddsh.ddspf.dwRBitMask == 0x000000FF)
+ && (ddsh.ddspf.dwGBitMask == 0x0000FF00)
+ && (ddsh.ddspf.dwBBitMask == 0x00FF0000)
+ && (ddsh.ddspf.dwABitMask == 0xFF000000)) {
+ // We support D3D's A8B8G8R8, which is actually RGBA in linear
+ // memory, equivalent to GL's RGBA
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ image->components = 4;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 4;
+ image->alpha = 1;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 32) && (ddsh.ddspf.dwRBitMask == 0x00FF0000)
+ && (ddsh.ddspf.dwGBitMask == 0x0000FF00)
+ && (ddsh.ddspf.dwBBitMask == 0x000000FF)
+ && (ddsh.ddspf.dwABitMask == 0xFF000000)) {
+ // We support D3D's A8R8G8B8, which is actually BGRA in linear
+ // memory, need to be
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ image->components = 4;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 4;
+ image->alpha = 1;
+ image->compressed = 0;
+ needsBGRASwap = true;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 16) && (ddsh.ddspf.dwRBitMask == 0x0000F800)
+ && (ddsh.ddspf.dwGBitMask == 0x000007E0)
+ && (ddsh.ddspf.dwBBitMask == 0x0000001F)
+ && (ddsh.ddspf.dwABitMask == 0x00000000)) {
+ // We support D3D's R5G6B5, which is actually RGB in linear
+ // memory. It is equivalent to GL's GL_UNSIGNED_SHORT_5_6_5
+ image->format = qt3ds::render::NVRenderTextureFormats::RGB8;
+ image->components = 3;
+ image->alpha = 0;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU16;
+ image->bytesPerPixel = 2;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGB8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 8) && (ddsh.ddspf.dwRBitMask == 0x00000000)
+ && (ddsh.ddspf.dwGBitMask == 0x00000000)
+ && (ddsh.ddspf.dwBBitMask == 0x00000000)
+ && (ddsh.ddspf.dwABitMask == 0x000000FF)) {
+ // We support D3D's A8
+ image->format = qt3ds::render::NVRenderTextureFormats::Alpha8;
+ image->components = 1;
+ image->alpha = 1;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 1;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::Alpha8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 8) && (ddsh.ddspf.dwRBitMask == 0x000000FF)
+ && (ddsh.ddspf.dwGBitMask == 0x00000000)
+ && (ddsh.ddspf.dwBBitMask == 0x00000000)
+ && (ddsh.ddspf.dwABitMask == 0x00000000)) {
+ // We support D3D's L8 (flagged as 8 bits of red only)
+ image->format = qt3ds::render::NVRenderTextureFormats::Luminance8;
+ image->components = 1;
+ image->alpha = 0;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 1;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::Luminance8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 16) && (ddsh.ddspf.dwRBitMask == 0x000000FF)
+ && (ddsh.ddspf.dwGBitMask == 0x00000000)
+ && (ddsh.ddspf.dwBBitMask == 0x00000000)
+ && (ddsh.ddspf.dwABitMask == 0x0000FF00)) {
+ // We support D3D's A8L8 (flagged as 8 bits of red and 8 bits of alpha)
+ image->format = qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8;
+ image->components = 2;
+ image->alpha = 1;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 2;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8;
+ } else {
+ throw "not a DXTC or supported RGB(A) format image";
+ }
+ }
+
+ // detect flagging to indicate this texture was stored in a y-inverted fashion
+ if (!(ddsh.dwFlags & DDS_LINEARSIZE)) {
+ if (ddsh.dwPitchOrLinearSize == DDS_MAGIC_FLIPPED) {
+ isAllreadyFlipped = true;
+ }
+ }
+
+ flipVertical = (isAllreadyFlipped != (flipVertical ? true : false)) ? 1 : 0;
+
+ // store primary surface width/height/numMipmaps
+ image->width = ddsh.dwWidth;
+ image->height = ddsh.dwHeight;
+ image->numMipmaps = ddsh.dwFlags & DDS_MIPMAPCOUNT ? ddsh.dwMipMapCount : 1;
+
+ if (image->numMipmaps > QT3DS_DDS_MAX_MIPMAPS) {
+ throw "Too many mipmaps: max 16";
+ }
+
+ // allocate the meta datablock for all mip storage.
+ Qt3DSDDSAllocDataBlock(io, image);
+ if (image->dataBlock == NULL) {
+ throw "Failed to allocate memory for image data storage";
+ }
+
+ dib->width = image->width;
+ dib->height = image->height;
+ dib->dds = image;
+
+ QT3DSI32 faces = image->cubemap ? 6 : 1;
+
+ QT3DSI32 index = 0;
+ for (QT3DSI32 j = 0; j < faces; j++) {
+ // load all surfaces for the image
+ QT3DSI32 width = image->width;
+ QT3DSI32 height = image->height;
+
+ for (QT3DSI32 i = 0; i < image->numMipmaps; i++) {
+ // Get the size, read in the data.
+ inStream.Read(
+ NVDataRef<QT3DSU8>((QT3DSU8 *)image->data[index], (QT3DSU32)image->size[index]));
+
+ // Flip in Y for OpenGL if needed
+ if (flipVertical)
+ flip_data_vertical(io, (QT3DSI8 *)image->data[index], width, height, image);
+
+ // shrink to next power of 2
+ width >>= 1;
+ height >>= 1;
+
+ if (!width)
+ width = 1;
+
+ if (!height)
+ height = 1;
+
+ // make sure DXT isn't <4 on a side...
+ if (image->compressed) {
+ if (width < 4)
+ width = 4;
+ if (height < 4)
+ height = 4;
+ }
+
+ index++;
+ }
+ }
+
+ if (needsBGRASwap) {
+ QT3DSI32 index = 0;
+ QT3DSI32 k;
+
+ for (k = 0; k < faces; k++) {
+ QT3DSI32 width = image->width;
+ QT3DSI32 height = image->height;
+
+ for (QT3DSI32 i = 0; i < image->numMipmaps; i++) {
+ QT3DSI8 *data = (QT3DSI8 *)(image->data[index]);
+ QT3DSI32 pixels = width * height;
+ QT3DSI32 j;
+
+ for (j = 0; j < pixels; j++) {
+ QT3DSI8 temp = data[0];
+ data[0] = data[2];
+ data[2] = temp;
+
+ data += 4;
+ }
+
+ // shrink to next power of 2
+ width >>= 1;
+ height >>= 1;
+
+ if (!width)
+ width = 1;
+
+ if (!height)
+ height = 1;
+
+ index++;
+ }
+ }
+ }
+ } catch (const char *message) {
+ if (image) {
+ if (image->dataBlock)
+ QT3DS_FREE(io->m_Allocator, image->dataBlock);
+
+ QT3DS_FREE(io->m_Allocator, image);
+ }
+ if (dib) {
+ FreeImage_Unload(dib);
+ }
+ if (message) {
+ FreeImage_OutputMessageProc(s_exception_string, message, io);
+ }
+ }
+
+ return dib;
+ }
+
+ SLoadedTexture *SLoadedTexture::LoadDDS(IInStream &inStream, QT3DSI32 flipVertical,
+ NVFoundationBase &inFnd,
+ qt3ds::render::NVRenderContextType renderContextType)
+ {
+ Q_UNUSED(renderContextType)
+ FreeImageIO theIO(inFnd.getAllocator(), inFnd);
+ SLoadedTexture *retval = DoLoadDDS(&theIO, inStream, flipVertical);
+
+ return retval;
+ }
+}
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.h b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.h
new file mode 100644
index 0000000..8490b47
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+#ifndef QT3DS_RENDER_LOAD_DDS_H
+#define QT3DS_RENDER_LOAD_DDS_H
+
+namespace qt3ds {
+namespace render {
+
+/** The maximum number of mipmap levels (per texture or per cubemap face) */
+#define QT3DS_DDS_MAX_MIPMAPS (16)
+
+/** The number of cubemap faces that must exist in a cubemap-bearing DDS file */
+#define QT3DS_DDS_NUM_CUBEMAP_FACES (6)
+
+ /** The master DDS structure for loading and saving
+
+ This is the master DDS structure. It shouldn't be allocated by hand,
+ always use NVHHDDSAlloc/NVHHDDSAllocData/NVHHDDSFree to manage them properly.
+ */
+
+ struct Qt3DSDDSImage
+ {
+ /** Width of the overall texture in texels */
+ int width;
+ /** Height of the overall texture in texels */
+ int height;
+ /** Number of color/alpha components per texel 1-4 */
+ int components;
+ /** The GL type of each color component (noncompressed textures only) */
+ int componentFormat;
+ /** The number of bytes per pixel (noncompressed textures only) */
+ int bytesPerPixel;
+ /** Nonzero if the format is DXT-compressed */
+ int compressed;
+ /** The number of levels in the mipmap pyramid (including the base level) */
+ int numMipmaps;
+ /** If nonzero, then the file contains 6 cubemap faces */
+ int cubemap;
+ /** The format of the loaded texture data */
+ int format;
+ /** The GL internal format of the loaded texture data(compressed textures only) */
+ int internalFormat;
+ /** Nonzero if the texture data includes alpha */
+ int alpha;
+ /** Base of the allocated block of all texel data */
+ void *dataBlock;
+ /** Pointers to the mipmap levels for the texture or each cubemap face */
+ void *data[QT3DS_DDS_MAX_MIPMAPS * QT3DS_DDS_NUM_CUBEMAP_FACES];
+ /** Array of sizes of the mipmap levels for the texture or each cubemap face */
+ int size[QT3DS_DDS_MAX_MIPMAPS * QT3DS_DDS_NUM_CUBEMAP_FACES];
+ /** Array of widths of the mipmap levels for the texture or each cubemap face */
+ int mipwidth[QT3DS_DDS_MAX_MIPMAPS * QT3DS_DDS_NUM_CUBEMAP_FACES];
+ /** Array of heights of the mipmap levels for the texture or each cubemap face */
+ int mipheight[QT3DS_DDS_MAX_MIPMAPS * QT3DS_DDS_NUM_CUBEMAP_FACES];
+ };
+}
+}
+
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureFreeImageCompat.h b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureFreeImageCompat.h
new file mode 100644
index 0000000..13f317b
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureFreeImageCompat.h
@@ -0,0 +1,413 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_RENDER_LOADED_TEXTURE_FREEIMAGE_COMPAT_H
+#define QT3DS_RENDER_LOADED_TEXTURE_FREEIMAGE_COMPAT_H
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "Qt3DSRenderLoadedTexture.h"
+#include "EASTL/algorithm.h"
+#include <stdlib.h>
+#ifndef _MACOSX
+#ifndef _INTEGRITYPLATFORM
+#include <malloc.h>
+#endif
+#endif
+
+// We use a compatibility layer so we can easily convert freeimage code to load our texture formats
+// where necessary.
+
+namespace qt3ds {
+namespace render {
+ using namespace qt3ds::foundation;
+
+ typedef int32_t BOOL;
+ typedef uint8_t BYTE;
+ typedef uint16_t WORD;
+ typedef uint32_t DWORD;
+ typedef int32_t LONG;
+
+#define FREEIMAGE_COLORORDER_BGR 0
+#define FREEIMAGE_COLORORDER_RGB 1
+
+#define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_BGR
+
+ typedef ISeekableIOStream *fi_handle;
+
+ struct FreeImageIO
+ {
+ NVAllocatorCallback &m_Allocator;
+ NVFoundationBase &m_Foundation;
+ int (*read_proc)(void *data, int size, int itemSize, fi_handle handle);
+ void (*seek_proc)(fi_handle handle, int offset, int pos);
+ int (*tell_proc)(fi_handle handle);
+ static inline int reader(void *data, int size, int itemSize, fi_handle handle)
+ {
+ NVDataRef<QT3DSU8> theData(toDataRef((QT3DSU8 *)data, (QT3DSU32)size * itemSize));
+ QT3DSU32 amount = handle->Read(theData);
+ return (int)amount;
+ }
+ static inline void seeker(fi_handle handle, int offset, int pos)
+ {
+ SeekPosition::Enum seekPos(SeekPosition::Begin);
+ /*
+#define SEEK_CUR 1
+#define SEEK_END 2
+#define SEEK_SET 0*/
+ switch (pos) {
+ case 0:
+ seekPos = SeekPosition::Begin;
+ break;
+ case 1:
+ seekPos = SeekPosition::Current;
+ break;
+ case 2:
+ seekPos = SeekPosition::End;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ handle->SetPosition(offset, seekPos);
+ }
+ static inline int teller(fi_handle handle) { return (int)handle->GetPosition(); }
+ FreeImageIO(NVAllocatorCallback &alloc, NVFoundationBase &fnd)
+ : m_Allocator(alloc)
+ , m_Foundation(fnd)
+ , read_proc(reader)
+ , seek_proc(seeker)
+ , tell_proc(teller)
+ {
+ }
+ };
+
+ typedef SLoadedTexture FIBITMAP;
+ inline BYTE *FreeImage_GetBits(FIBITMAP *bmp) { return (BYTE *)bmp->data; }
+
+ inline int FreeImage_GetHeight(FIBITMAP *bmp) { return bmp->height; }
+ inline int FreeImage_GetWidth(FIBITMAP *bmp) { return bmp->width; }
+
+#define INPLACESWAP(x, y) eastl::swap(x, y)
+#define MIN(x, y) NVMin(x, y)
+#define MAX(x, y) NVMax(x, y)
+
+#define TRUE 1
+#define FALSE 0
+
+ typedef struct tagBITMAPINFOHEADER
+ {
+ DWORD biSize;
+ LONG biWidth;
+ LONG biHeight;
+ WORD biPlanes;
+ WORD biBitCount;
+ DWORD biCompression;
+ DWORD biSizeImage;
+ LONG biXPelsPerMeter;
+ LONG biYPelsPerMeter;
+ DWORD biClrUsed;
+ DWORD biClrImportant;
+ } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
+
+ typedef struct tagRGBQUAD
+ {
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ BYTE rgbBlue;
+ BYTE rgbGreen;
+ BYTE rgbRed;
+#else
+ BYTE rgbRed;
+ BYTE rgbGreen;
+ BYTE rgbBlue;
+#endif // FREEIMAGE_COLORORDER
+ BYTE rgbReserved;
+ } RGBQUAD;
+
+ typedef struct tagRGBTRIPLE
+ {
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ BYTE rgbtBlue;
+ BYTE rgbtGreen;
+ BYTE rgbtRed;
+#else
+ BYTE rgbtRed;
+ BYTE rgbtGreen;
+ BYTE rgbtBlue;
+#endif // FREEIMAGE_COLORORDER
+ } RGBTRIPLE;
+
+ typedef struct tagBITMAPINFO
+ {
+ BITMAPINFOHEADER bmiHeader;
+ RGBQUAD bmiColors[1];
+ } BITMAPINFO, *PBITMAPINFO;
+
+ typedef struct tagFILE_RGBA
+ {
+ unsigned char r, g, b, a;
+ } FILE_RGBA;
+
+ typedef struct tagFILE_BGRA
+ {
+ unsigned char b, g, r, a;
+ } FILE_BGRA;
+
+ typedef struct tagFILE_RGB
+ {
+ unsigned char r, g, b;
+ } FILE_RGB;
+
+ typedef struct tagFILE_BGR
+ {
+ unsigned char b, g, r;
+ } FILE_BGR;
+
+// Indexes for byte arrays, masks and shifts for treating pixels as words ---
+// These coincide with the order of RGBQUAD and RGBTRIPLE -------------------
+
+#ifndef FREEIMAGE_BIGENDIAN
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+// Little Endian (x86 / MS Windows, Linux) : BGR(A) order
+#define FI_RGBA_RED 2
+#define FI_RGBA_GREEN 1
+#define FI_RGBA_BLUE 0
+#define FI_RGBA_ALPHA 3
+#define FI_RGBA_RED_MASK 0x00FF0000
+#define FI_RGBA_GREEN_MASK 0x0000FF00
+#define FI_RGBA_BLUE_MASK 0x000000FF
+#define FI_RGBA_ALPHA_MASK 0xFF000000
+#define FI_RGBA_RED_SHIFT 16
+#define FI_RGBA_GREEN_SHIFT 8
+#define FI_RGBA_BLUE_SHIFT 0
+#define FI_RGBA_ALPHA_SHIFT 24
+#else
+// Little Endian (x86 / MaxOSX) : RGB(A) order
+#define FI_RGBA_RED 0
+#define FI_RGBA_GREEN 1
+#define FI_RGBA_BLUE 2
+#define FI_RGBA_ALPHA 3
+#define FI_RGBA_RED_MASK 0x000000FF
+#define FI_RGBA_GREEN_MASK 0x0000FF00
+#define FI_RGBA_BLUE_MASK 0x00FF0000
+#define FI_RGBA_ALPHA_MASK 0xFF000000
+#define FI_RGBA_RED_SHIFT 0
+#define FI_RGBA_GREEN_SHIFT 8
+#define FI_RGBA_BLUE_SHIFT 16
+#define FI_RGBA_ALPHA_SHIFT 24
+#endif // FREEIMAGE_COLORORDER
+#else
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+// Big Endian (PPC / none) : BGR(A) order
+#define FI_RGBA_RED 2
+#define FI_RGBA_GREEN 1
+#define FI_RGBA_BLUE 0
+#define FI_RGBA_ALPHA 3
+#define FI_RGBA_RED_MASK 0x0000FF00
+#define FI_RGBA_GREEN_MASK 0x00FF0000
+#define FI_RGBA_BLUE_MASK 0xFF000000
+#define FI_RGBA_ALPHA_MASK 0x000000FF
+#define FI_RGBA_RED_SHIFT 8
+#define FI_RGBA_GREEN_SHIFT 16
+#define FI_RGBA_BLUE_SHIFT 24
+#define FI_RGBA_ALPHA_SHIFT 0
+#else
+// Big Endian (PPC / Linux, MaxOSX) : RGB(A) order
+#define FI_RGBA_RED 0
+#define FI_RGBA_GREEN 1
+#define FI_RGBA_BLUE 2
+#define FI_RGBA_ALPHA 3
+#define FI_RGBA_RED_MASK 0xFF000000
+#define FI_RGBA_GREEN_MASK 0x00FF0000
+#define FI_RGBA_BLUE_MASK 0x0000FF00
+#define FI_RGBA_ALPHA_MASK 0x000000FF
+#define FI_RGBA_RED_SHIFT 24
+#define FI_RGBA_GREEN_SHIFT 16
+#define FI_RGBA_BLUE_SHIFT 8
+#define FI_RGBA_ALPHA_SHIFT 0
+#endif // FREEIMAGE_COLORORDER
+#endif // FREEIMAGE_BIGENDIAN
+
+#define FI_RGBA_RGB_MASK (FI_RGBA_RED_MASK | FI_RGBA_GREEN_MASK | FI_RGBA_BLUE_MASK)
+
+// The 16bit macros only include masks and shifts, since each color element is not byte aligned
+
+#define FI16_555_RED_MASK 0x7C00
+#define FI16_555_GREEN_MASK 0x03E0
+#define FI16_555_BLUE_MASK 0x001F
+#define FI16_555_RED_SHIFT 10
+#define FI16_555_GREEN_SHIFT 5
+#define FI16_555_BLUE_SHIFT 0
+#define FI16_565_RED_MASK 0xF800
+#define FI16_565_GREEN_MASK 0x07E0
+#define FI16_565_BLUE_MASK 0x001F
+#define FI16_565_RED_SHIFT 11
+#define FI16_565_GREEN_SHIFT 5
+#define FI16_565_BLUE_SHIFT 0
+
+ inline unsigned char HINIBBLE(unsigned char byte) { return byte & 0xF0; }
+
+ inline unsigned char LOWNIBBLE(unsigned char byte) { return byte & 0x0F; }
+
+ inline int CalculateUsedBits(int bits)
+ {
+ int bit_count = 0;
+ unsigned bit = 1;
+
+ for (unsigned i = 0; i < 32; i++) {
+ if ((bits & bit) == bit) {
+ bit_count++;
+ }
+
+ bit <<= 1;
+ }
+
+ return bit_count;
+ }
+
+ inline int CalculateLine(int width, int bitdepth) { return ((width * bitdepth) + 7) / 8; }
+
+ inline int CalculatePitch(int line) { return (line + 3) & ~3; }
+
+ inline int CalculateUsedPaletteEntries(int bit_count)
+ {
+ if ((bit_count >= 1) && (bit_count <= 8))
+ return 1 << bit_count;
+
+ return 0;
+ }
+
+ inline unsigned char *CalculateScanLine(unsigned char *bits, unsigned pitch, int scanline)
+ {
+ return (bits + (pitch * scanline));
+ }
+
+ inline void ReplaceExtension(char *result, const char *filename, const char *extension)
+ {
+ for (size_t i = strlen(filename) - 1; i > 0; --i) {
+ if (filename[i] == '.') {
+ memcpy(result, filename, i);
+ result[i] = '.';
+ memcpy(result + i + 1, extension, strlen(extension) + 1);
+ return;
+ }
+ }
+
+ memcpy(result, filename, strlen(filename));
+ result[strlen(filename)] = '.';
+ memcpy(result + strlen(filename) + 1, extension, strlen(extension) + 1);
+ }
+
+ inline BYTE *FreeImage_GetScanLine(FIBITMAP *bmp, int height)
+ {
+ return CalculateScanLine(
+ (BYTE *)bmp->data, CalculatePitch(CalculateLine(bmp->width, bmp->m_BitCount)), height);
+ }
+
+#define DLL_CALLCONV
+
+// ignored for now.
+#define FreeImage_SetDotsPerMeterX(img, dots)
+#define FreeImage_SetDotsPerMeterY(img, dots)
+
+ inline SLoadedTexture *FreeImage_Allocate(int width, int height, int bit_count, FreeImageIO *io)
+ {
+ SLoadedTexture *theTexture = QT3DS_NEW(io->m_Allocator, SLoadedTexture)(io->m_Allocator);
+ int pitch = CalculatePitch(CalculateLine(width, bit_count));
+ QT3DSU32 dataSize = (QT3DSU32)(height * pitch);
+ theTexture->dataSizeInBytes = dataSize;
+ theTexture->data = io->m_Allocator.allocate(dataSize, "image data", __FILE__, __LINE__);
+ memZero(theTexture->data, dataSize);
+ theTexture->width = width;
+ theTexture->height = height;
+ theTexture->m_BitCount = bit_count;
+ // If free image asks us for a palette, we change our format at that time.
+ theTexture->m_ExtendedFormat = ExtendedTextureFormats::CustomRGB;
+ return theTexture;
+ }
+
+ inline SLoadedTexture *FreeImage_Allocate(int width, int height, int bit_count, int rmask,
+ int gmask, int bmask, FreeImageIO *io)
+ {
+ SLoadedTexture *retval = FreeImage_Allocate(width, height, bit_count, io);
+ retval->m_CustomMasks[0] = rmask;
+ retval->m_CustomMasks[1] = gmask;
+ retval->m_CustomMasks[2] = bmask;
+ return retval;
+ }
+
+ inline RGBQUAD *FreeImage_GetPalette(SLoadedTexture *texture)
+ {
+ if (texture->m_Palette == NULL) {
+ texture->m_ExtendedFormat = ExtendedTextureFormats::Palettized;
+ QT3DSU32 memory = 256 * sizeof(RGBQUAD);
+ if (memory) {
+ texture->m_Palette =
+ texture->m_Allocator.allocate(memory, "texture palette", __FILE__, __LINE__);
+ memZero(texture->m_Palette, memory);
+ }
+ }
+ return (RGBQUAD *)texture->m_Palette;
+ }
+
+ inline void FreeImage_Unload(SLoadedTexture *texture) { texture->release(); }
+ inline void FreeImage_OutputMessageProc(int, const char *message, FreeImageIO *io)
+ {
+ Q_UNUSED(io);
+ qCCritical(INVALID_OPERATION, "Error loading image: %s", message);
+ }
+
+ inline void FreeImage_SetBackgroundColor(SLoadedTexture *texture, RGBQUAD *inColor)
+ {
+ if (inColor) {
+ texture->m_BackgroundColor[0] = inColor->rgbRed;
+ texture->m_BackgroundColor[1] = inColor->rgbGreen;
+ texture->m_BackgroundColor[2] = inColor->rgbBlue;
+ } else
+ memSet(texture->m_BackgroundColor, 0, 3);
+ }
+
+ inline void FreeImage_SetTransparencyTable(SLoadedTexture *texture, BYTE *table, int size)
+ {
+ if (texture->m_TransparencyTable)
+ texture->m_Allocator.deallocate(texture->m_TransparencyTable);
+ texture->m_TransparencyTable = NULL;
+ if (table && size) {
+ texture->m_TransparencyTable = (uint8_t *)texture->m_Allocator.allocate(
+ size, "texture transparency table", __FILE__, __LINE__);
+ memCopy(texture->m_TransparencyTable, table, size);
+ }
+ }
+}
+}
+
+using namespace qt3ds::render;
+
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp
new file mode 100644
index 0000000..ece4d3a
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp
@@ -0,0 +1,851 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+// ==========================================================
+// GIF Loader and Writer
+//
+// Design and implementation by
+// - Ryan Rubley <ryan@lostreality.org>
+// - Raphael Gaquer <raphael.gaquer@alcer.com>
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+#ifdef _MSC_VER
+#pragma warning(disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "Qt3DSRenderLoadedTextureFreeImageCompat.h"
+#include "EASTL/vector.h"
+#include "EASTL/string.h"
+// ==========================================================
+// Metadata declarations
+// ==========================================================
+
+#define GIF_DISPOSAL_UNSPECIFIED 0
+#define GIF_DISPOSAL_LEAVE 1
+#define GIF_DISPOSAL_BACKGROUND 2
+#define GIF_DISPOSAL_PREVIOUS 3
+
+// ==========================================================
+// Constant/Typedef declarations
+// ==========================================================
+
+struct GIFinfo
+{
+ BOOL read;
+ // only really used when reading
+ size_t global_color_table_offset;
+ int global_color_table_size;
+ BYTE background_color;
+ eastl::vector<size_t> application_extension_offsets;
+ eastl::vector<size_t> comment_extension_offsets;
+ eastl::vector<size_t> graphic_control_extension_offsets;
+ eastl::vector<size_t> image_descriptor_offsets;
+
+ GIFinfo()
+ : read(0)
+ , global_color_table_offset(0)
+ , global_color_table_size(0)
+ , background_color(0)
+ {
+ }
+};
+
+struct PageInfo
+{
+ PageInfo(int d, int l, int t, int w, int h)
+ {
+ disposal_method = d;
+ left = (WORD)l;
+ top = (WORD)t;
+ width = (WORD)w;
+ height = (WORD)h;
+ }
+ int disposal_method;
+ WORD left, top, width, height;
+};
+
+// GIF defines a max of 12 bits per code
+#define MAX_LZW_CODE 4096
+
+class StringTable
+{
+public:
+ StringTable();
+ ~StringTable();
+ void Initialize(int minCodeSize);
+ BYTE *FillInputBuffer(int len);
+ void CompressStart(int bpp, int width);
+ int CompressEnd(BYTE *buf); // 0-4 bytes
+ bool Compress(BYTE *buf, int *len);
+ bool Decompress(BYTE *buf, int *len);
+ void Done(void);
+
+protected:
+ bool m_done;
+
+ int m_minCodeSize, m_clearCode, m_endCode, m_nextCode;
+
+ int m_bpp, m_slack; // Compressor information
+
+ int m_prefix; // Compressor state variable
+ int m_codeSize, m_codeMask; // Compressor/Decompressor state variables
+ int m_oldCode; // Decompressor state variable
+ int m_partial, m_partialSize; // Compressor/Decompressor bit buffer
+
+ int firstPixelPassed; // A specific flag that indicates if the first pixel
+ // of the whole image had already been read
+
+ eastl::string m_strings[MAX_LZW_CODE]; // This is what is really the "string table" data for the
+ // Decompressor
+ int *m_strmap;
+
+ // input buffer
+ BYTE *m_buffer;
+ int m_bufferSize, m_bufferRealSize, m_bufferPos, m_bufferShift;
+
+ void ClearCompressorTable(void);
+ void ClearDecompressorTable(void);
+};
+
+#define GIF_PACKED_LSD_HAVEGCT 0x80
+#define GIF_PACKED_LSD_COLORRES 0x70
+#define GIF_PACKED_LSD_GCTSORTED 0x08
+#define GIF_PACKED_LSD_GCTSIZE 0x07
+#define GIF_PACKED_ID_HAVELCT 0x80
+#define GIF_PACKED_ID_INTERLACED 0x40
+#define GIF_PACKED_ID_LCTSORTED 0x20
+#define GIF_PACKED_ID_RESERVED 0x18
+#define GIF_PACKED_ID_LCTSIZE 0x07
+#define GIF_PACKED_GCE_RESERVED 0xE0
+#define GIF_PACKED_GCE_DISPOSAL 0x1C
+#define GIF_PACKED_GCE_WAITINPUT 0x02
+#define GIF_PACKED_GCE_HAVETRANS 0x01
+
+#define GIF_BLOCK_IMAGE_DESCRIPTOR 0x2C
+#define GIF_BLOCK_EXTENSION 0x21
+#define GIF_BLOCK_TRAILER 0x3B
+
+#define GIF_EXT_PLAINTEXT 0x01
+#define GIF_EXT_GRAPHIC_CONTROL 0xF9
+#define GIF_EXT_COMMENT 0xFE
+#define GIF_EXT_APPLICATION 0xFF
+
+#define GIF_INTERLACE_PASSES 4
+static int g_GifInterlaceOffset[GIF_INTERLACE_PASSES] = { 0, 4, 2, 1 };
+static int g_GifInterlaceIncrement[GIF_INTERLACE_PASSES] = { 8, 8, 4, 2 };
+
+StringTable::StringTable()
+{
+ m_buffer = NULL;
+ firstPixelPassed = 0; // Still no pixel read
+ // Maximum number of entries in the map is MAX_LZW_CODE * 256
+ // (aka 2**12 * 2**8 => a 20 bits key)
+ // This Map could be optmized to only handle MAX_LZW_CODE * 2**(m_bpp)
+ m_strmap = (int *)new int[1 << 20];
+}
+
+StringTable::~StringTable()
+{
+ if (m_buffer != NULL) {
+ delete[] m_buffer;
+ }
+ if (m_strmap != NULL) {
+ delete[] m_strmap;
+ m_strmap = NULL;
+ }
+}
+
+void StringTable::Initialize(int minCodeSize)
+{
+ m_done = false;
+
+ m_bpp = 8;
+ m_minCodeSize = minCodeSize;
+ m_clearCode = 1 << m_minCodeSize;
+ if (m_clearCode > MAX_LZW_CODE) {
+ m_clearCode = MAX_LZW_CODE;
+ }
+ m_endCode = m_clearCode + 1;
+
+ m_partial = 0;
+ m_partialSize = 0;
+
+ m_bufferSize = 0;
+ ClearCompressorTable();
+ ClearDecompressorTable();
+}
+
+BYTE *StringTable::FillInputBuffer(int len)
+{
+ if (m_buffer == NULL) {
+ m_buffer = new BYTE[len];
+ m_bufferRealSize = len;
+ } else if (len > m_bufferRealSize) {
+ delete[] m_buffer;
+ m_buffer = new BYTE[len];
+ m_bufferRealSize = len;
+ }
+ m_bufferSize = len;
+ m_bufferPos = 0;
+ m_bufferShift = 8 - m_bpp;
+ return m_buffer;
+}
+
+void StringTable::CompressStart(int bpp, int width)
+{
+ m_bpp = bpp;
+ m_slack = (8 - ((width * bpp) % 8)) % 8;
+
+ m_partial |= m_clearCode << m_partialSize;
+ m_partialSize += m_codeSize;
+ ClearCompressorTable();
+}
+
+int StringTable::CompressEnd(BYTE *buf)
+{
+ int len = 0;
+
+ // output code for remaining prefix
+ m_partial |= m_prefix << m_partialSize;
+ m_partialSize += m_codeSize;
+ while (m_partialSize >= 8) {
+ *buf++ = (BYTE)m_partial;
+ m_partial >>= 8;
+ m_partialSize -= 8;
+ len++;
+ }
+
+ // add the end of information code and flush the entire buffer out
+ m_partial |= m_endCode << m_partialSize;
+ m_partialSize += m_codeSize;
+ while (m_partialSize > 0) {
+ *buf++ = (BYTE)m_partial;
+ m_partial >>= 8;
+ m_partialSize -= 8;
+ len++;
+ }
+
+ // most this can be is 4 bytes. 7 bits in m_partial to start + 12 for the
+ // last code + 12 for the end code = 31 bits total.
+ return len;
+}
+
+bool StringTable::Compress(BYTE *buf, int *len)
+{
+ if (m_bufferSize == 0 || m_done) {
+ return false;
+ }
+
+ int mask = (1 << m_bpp) - 1;
+ BYTE *bufpos = buf;
+ while (m_bufferPos < m_bufferSize) {
+ // get the current pixel value
+ char ch = (char)((m_buffer[m_bufferPos] >> m_bufferShift) & mask);
+
+ // The next prefix is :
+ // <the previous LZW code (on 12 bits << 8)> | <the code of the current pixel (on 8 bits)>
+ int nextprefix = (((m_prefix) << 8) & 0xFFF00) + (ch & 0x000FF);
+ if (firstPixelPassed) {
+
+ if (m_strmap[nextprefix] > 0) {
+ m_prefix = m_strmap[nextprefix];
+ } else {
+ m_partial |= m_prefix << m_partialSize;
+ m_partialSize += m_codeSize;
+ // grab full bytes for the output buffer
+ while (m_partialSize >= 8 && bufpos - buf < *len) {
+ *bufpos++ = (BYTE)m_partial;
+ m_partial >>= 8;
+ m_partialSize -= 8;
+ }
+
+ // add the code to the "table map"
+ m_strmap[nextprefix] = m_nextCode;
+
+ // increment the next highest valid code, increase the code size
+ if (m_nextCode == (1 << m_codeSize)) {
+ m_codeSize++;
+ }
+ m_nextCode++;
+
+ // if we're out of codes, restart the string table
+ if (m_nextCode == MAX_LZW_CODE) {
+ m_partial |= m_clearCode << m_partialSize;
+ m_partialSize += m_codeSize;
+ ClearCompressorTable();
+ }
+
+ // Only keep the 8 lowest bits (prevent problems with "negative chars")
+ m_prefix = ch & 0x000FF;
+ }
+
+ // increment to the next pixel
+ if (m_bufferShift > 0
+ && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack)) {
+ m_bufferShift -= m_bpp;
+ } else {
+ m_bufferPos++;
+ m_bufferShift = 8 - m_bpp;
+ }
+
+ // jump out here if the output buffer is full
+ if (bufpos - buf == *len) {
+ return true;
+ }
+
+ } else {
+ // Specific behavior for the first pixel of the whole image
+
+ firstPixelPassed = 1;
+ // Only keep the 8 lowest bits (prevent problems with "negative chars")
+ m_prefix = ch & 0x000FF;
+
+ // increment to the next pixel
+ if (m_bufferShift > 0
+ && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack)) {
+ m_bufferShift -= m_bpp;
+ } else {
+ m_bufferPos++;
+ m_bufferShift = 8 - m_bpp;
+ }
+
+ // jump out here if the output buffer is full
+ if (bufpos - buf == *len) {
+ return true;
+ }
+ }
+ }
+
+ m_bufferSize = 0;
+ *len = (int)(bufpos - buf);
+
+ return true;
+}
+
+bool StringTable::Decompress(BYTE *buf, int *len)
+{
+ if (m_bufferSize == 0 || m_done) {
+ return false;
+ }
+
+ BYTE *bufpos = buf;
+ for (; m_bufferPos < m_bufferSize; m_bufferPos++) {
+ m_partial |= (int)m_buffer[m_bufferPos] << m_partialSize;
+ m_partialSize += 8;
+ while (m_partialSize >= m_codeSize) {
+ int code = m_partial & m_codeMask;
+ m_partial >>= m_codeSize;
+ m_partialSize -= m_codeSize;
+
+ if (code > m_nextCode || (m_nextCode == MAX_LZW_CODE && code != m_clearCode)
+ || code == m_endCode) {
+ m_done = true;
+ *len = (int)(bufpos - buf);
+ return true;
+ }
+ if (code == m_clearCode) {
+ ClearDecompressorTable();
+ continue;
+ }
+
+ // add new string to string table, if not the first pass since a clear code
+ if (m_oldCode != MAX_LZW_CODE) {
+ m_strings[m_nextCode] =
+ m_strings[m_oldCode] + m_strings[code == m_nextCode ? m_oldCode : code][0];
+ }
+
+ if ((int)m_strings[code].size() > *len - (bufpos - buf)) {
+ // out of space, stuff the code back in for next time
+ m_partial <<= m_codeSize;
+ m_partialSize += m_codeSize;
+ m_partial |= code;
+ m_bufferPos++;
+ *len = (int)(bufpos - buf);
+ return true;
+ }
+
+ // output the string into the buffer
+ memcpy(bufpos, m_strings[code].data(), m_strings[code].size());
+ bufpos += m_strings[code].size();
+
+ // increment the next highest valid code, add a bit to the mask if we need to increase
+ // the code size
+ if (m_oldCode != MAX_LZW_CODE && m_nextCode < MAX_LZW_CODE) {
+ if (++m_nextCode < MAX_LZW_CODE) {
+ if ((m_nextCode & m_codeMask) == 0) {
+ m_codeSize++;
+ m_codeMask |= m_nextCode;
+ }
+ }
+ }
+
+ m_oldCode = code;
+ }
+ }
+
+ m_bufferSize = 0;
+ *len = (int)(bufpos - buf);
+
+ return true;
+}
+
+void StringTable::Done(void)
+{
+ m_done = true;
+}
+
+void StringTable::ClearCompressorTable(void)
+{
+ if (m_strmap) {
+ memset(m_strmap, 0xFF, sizeof(unsigned int) * (1 << 20));
+ }
+ m_nextCode = m_endCode + 1;
+
+ m_prefix = 0;
+ m_codeSize = m_minCodeSize + 1;
+}
+
+void StringTable::ClearDecompressorTable(void)
+{
+ for (int i = 0; i < m_clearCode; i++) {
+ m_strings[i].resize(1);
+ m_strings[i][0] = (char)i;
+ }
+ m_nextCode = m_endCode + 1;
+
+ m_codeSize = m_minCodeSize + 1;
+ m_codeMask = (1 << m_codeSize) - 1;
+ m_oldCode = MAX_LZW_CODE;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static BOOL DLL_CALLCONV Validate(FreeImageIO *io, fi_handle handle)
+{
+ char buf[6];
+ if (io->read_proc(buf, 6, 1, handle) < 1) {
+ return FALSE;
+ }
+
+ BOOL bResult = FALSE;
+ if (!strncmp(buf, "GIF", 3)) {
+ if (buf[3] >= '0' && buf[3] <= '9' && buf[4] >= '0' && buf[4] <= '9' && buf[5] >= 'a'
+ && buf[5] <= 'z') {
+ bResult = TRUE;
+ }
+ }
+
+ io->seek_proc(handle, -6, SEEK_CUR);
+
+ return bResult;
+}
+
+// ----------------------------------------------------------
+
+static void *DLL_CALLCONV Open(FreeImageIO *io, fi_handle handle)
+{
+ GIFinfo *info = new GIFinfo;
+ if (info == NULL) {
+ return NULL;
+ }
+ BOOL read = TRUE;
+
+ // 25/02/2008 MDA: Not safe to memset GIFinfo structure with VS 2008 (safe iterators),
+ // perform initialization in constructor instead.
+ // memset(info, 0, sizeof(GIFinfo));
+
+ info->read = read;
+ try {
+ // Header
+ if (!Validate(io, handle)) {
+ throw "Not a GIF file";
+ }
+ io->seek_proc(handle, 6, SEEK_CUR);
+
+ // Logical Screen Descriptor
+ io->seek_proc(handle, 4, SEEK_CUR);
+ BYTE packed;
+ if (io->read_proc(&packed, 1, 1, handle) < 1) {
+ throw "EOF reading Logical Screen Descriptor";
+ }
+ if (io->read_proc(&info->background_color, 1, 1, handle) < 1) {
+ throw "EOF reading Logical Screen Descriptor";
+ }
+ io->seek_proc(handle, 1, SEEK_CUR);
+
+ // Global Color Table
+ if (packed & GIF_PACKED_LSD_HAVEGCT) {
+ info->global_color_table_offset = io->tell_proc(handle);
+ info->global_color_table_size = 2 << (packed & GIF_PACKED_LSD_GCTSIZE);
+ io->seek_proc(handle, 3 * info->global_color_table_size, SEEK_CUR);
+ }
+
+ // Scan through all the rest of the blocks, saving offsets
+ size_t gce_offset = 0;
+ BYTE block = 0;
+ while (block != GIF_BLOCK_TRAILER) {
+ if (io->read_proc(&block, 1, 1, handle) < 1) {
+ throw "EOF reading blocks";
+ }
+ if (block == GIF_BLOCK_IMAGE_DESCRIPTOR) {
+ info->image_descriptor_offsets.push_back(io->tell_proc(handle));
+ // GCE may be 0, meaning no GCE preceded this ID
+ info->graphic_control_extension_offsets.push_back(gce_offset);
+ gce_offset = 0;
+
+ io->seek_proc(handle, 8, SEEK_CUR);
+ if (io->read_proc(&packed, 1, 1, handle) < 1) {
+ throw "EOF reading Image Descriptor";
+ }
+
+ // Local Color Table
+ if (packed & GIF_PACKED_ID_HAVELCT) {
+ io->seek_proc(handle, 3 * (2 << (packed & GIF_PACKED_ID_LCTSIZE)), SEEK_CUR);
+ }
+
+ // LZW Minimum Code Size
+ io->seek_proc(handle, 1, SEEK_CUR);
+ } else if (block == GIF_BLOCK_EXTENSION) {
+ BYTE ext;
+ if (io->read_proc(&ext, 1, 1, handle) < 1) {
+ throw "EOF reading extension";
+ }
+
+ if (ext == GIF_EXT_GRAPHIC_CONTROL) {
+ // overwrite previous offset if more than one GCE found before an ID
+ gce_offset = io->tell_proc(handle);
+ } else if (ext == GIF_EXT_COMMENT) {
+ info->comment_extension_offsets.push_back(io->tell_proc(handle));
+ } else if (ext == GIF_EXT_APPLICATION) {
+ info->application_extension_offsets.push_back(io->tell_proc(handle));
+ }
+ } else if (block == GIF_BLOCK_TRAILER) {
+ continue;
+ } else {
+ throw "Invalid GIF block found";
+ }
+
+ // Data Sub-blocks
+ BYTE len;
+ if (io->read_proc(&len, 1, 1, handle) < 1) {
+ throw "EOF reading sub-block";
+ }
+ while (len != 0) {
+ io->seek_proc(handle, len, SEEK_CUR);
+ if (io->read_proc(&len, 1, 1, handle) < 1) {
+ throw "EOF reading sub-block";
+ }
+ }
+ }
+ } catch (const char *msg) {
+ FreeImage_OutputMessageProc(s_format_id, msg, io);
+ delete info;
+ return NULL;
+ }
+
+ return info;
+}
+
+static FIBITMAP *DLL_CALLCONV DoLoadGIF(FreeImageIO *io, fi_handle handle, int flags, void *data)
+{
+ if (data == NULL) {
+ return NULL;
+ }
+ (void)flags;
+ GIFinfo *info = (GIFinfo *)data;
+
+ int page = 0;
+ FIBITMAP *dib = NULL;
+ try {
+ bool have_transparent = false, no_local_palette = false, interlaced = false;
+ int disposal_method = GIF_DISPOSAL_LEAVE, transparent_color = 0;
+ WORD left, top, width, height;
+ BYTE packed, b;
+ WORD w;
+
+ // Image Descriptor
+ io->seek_proc(handle, (long)info->image_descriptor_offsets[page], SEEK_SET);
+ io->read_proc(&left, 2, 1, handle);
+ io->read_proc(&top, 2, 1, handle);
+ io->read_proc(&width, 2, 1, handle);
+ io->read_proc(&height, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&left);
+ SwapShort(&top);
+ SwapShort(&width);
+ SwapShort(&height);
+#endif
+ io->read_proc(&packed, 1, 1, handle);
+ interlaced = (packed & GIF_PACKED_ID_INTERLACED) ? true : false;
+ no_local_palette = (packed & GIF_PACKED_ID_HAVELCT) ? false : true;
+
+ int bpp = 8;
+ if (!no_local_palette) {
+ int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);
+ if (size <= 2)
+ bpp = 1;
+ else if (size <= 16)
+ bpp = 4;
+ } else if (info->global_color_table_offset != 0) {
+ if (info->global_color_table_size <= 2)
+ bpp = 1;
+ else if (info->global_color_table_size <= 16)
+ bpp = 4;
+ }
+ dib = FreeImage_Allocate(width, height, bpp, io);
+ if (dib == NULL) {
+ throw "DIB allocated failed";
+ }
+
+ // Palette
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ if (!no_local_palette) {
+ int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);
+
+ int i = 0;
+ while (i < size) {
+ io->read_proc(&pal[i].rgbRed, 1, 1, handle);
+ io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
+ io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
+ i++;
+ }
+ } else if (info->global_color_table_offset != 0) {
+ long pos = io->tell_proc(handle);
+ io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET);
+
+ int i = 0;
+ while (i < info->global_color_table_size) {
+ io->read_proc(&pal[i].rgbRed, 1, 1, handle);
+ io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
+ io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
+ i++;
+ }
+
+ io->seek_proc(handle, pos, SEEK_SET);
+ } else {
+ // its legal to have no palette, but we're going to generate *something*
+ for (int i = 0; i < 256; i++) {
+ pal[i].rgbRed = (BYTE)i;
+ pal[i].rgbGreen = (BYTE)i;
+ pal[i].rgbBlue = (BYTE)i;
+ }
+ }
+
+ // LZW Minimum Code Size
+ io->read_proc(&b, 1, 1, handle);
+ StringTable *stringtable = new StringTable;
+ stringtable->Initialize(b);
+
+ // Image Data Sub-blocks
+ int x = 0, xpos = 0, y = 0, shift = 8 - bpp, mask = (1 << bpp) - 1, interlacepass = 0;
+ BYTE *scanline = FreeImage_GetScanLine(dib, height - 1);
+ BYTE buf[4096];
+ io->read_proc(&b, 1, 1, handle);
+ while (b) {
+ io->read_proc(stringtable->FillInputBuffer(b), b, 1, handle);
+ int size = sizeof(buf);
+ while (stringtable->Decompress(buf, &size)) {
+ for (int i = 0; i < size; i++) {
+ scanline[xpos] |= (buf[i] & mask) << shift;
+ if (shift > 0) {
+ shift -= bpp;
+ } else {
+ xpos++;
+ shift = 8 - bpp;
+ }
+ if (++x >= width) {
+ if (interlaced) {
+ y += g_GifInterlaceIncrement[interlacepass];
+ if (y >= height && ++interlacepass < GIF_INTERLACE_PASSES) {
+ y = g_GifInterlaceOffset[interlacepass];
+ }
+ } else {
+ y++;
+ }
+ if (y >= height) {
+ stringtable->Done();
+ break;
+ }
+ x = xpos = 0;
+ shift = 8 - bpp;
+ scanline = FreeImage_GetScanLine(dib, height - y - 1);
+ }
+ }
+ size = sizeof(buf);
+ }
+ io->read_proc(&b, 1, 1, handle);
+ }
+
+ if (page == 0) {
+ QT3DSU32 idx;
+
+ // Logical Screen Descriptor
+ io->seek_proc(handle, 6, SEEK_SET);
+ WORD logicalwidth, logicalheight;
+ io->read_proc(&logicalwidth, 2, 1, handle);
+ io->read_proc(&logicalheight, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&logicalwidth);
+ SwapShort(&logicalheight);
+#endif
+
+ // Global Color Table
+ if (info->global_color_table_offset != 0) {
+ RGBQUAD globalpalette[256];
+ io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET);
+ int i = 0;
+ while (i < info->global_color_table_size) {
+ io->read_proc(&globalpalette[i].rgbRed, 1, 1, handle);
+ io->read_proc(&globalpalette[i].rgbGreen, 1, 1, handle);
+ io->read_proc(&globalpalette[i].rgbBlue, 1, 1, handle);
+ globalpalette[i].rgbReserved = 0;
+ i++;
+ }
+ // background color
+ if (info->background_color < info->global_color_table_size) {
+ FreeImage_SetBackgroundColor(dib, &globalpalette[info->background_color]);
+ }
+ }
+
+ // Application Extension
+ LONG loop = 1; // If no AE with a loop count is found, the default must be 1
+ for (idx = 0; idx < info->application_extension_offsets.size(); idx++) {
+ io->seek_proc(handle, (long)info->application_extension_offsets[idx], SEEK_SET);
+ io->read_proc(&b, 1, 1, handle);
+ if (b == 11) { // All AEs start with an 11 byte sub-block to determine what type of
+ // AE it is
+ char buf[11];
+ io->read_proc(buf, 11, 1, handle);
+ if (!memcmp(buf, "NETSCAPE2.0", 11)
+ || !memcmp(buf, "ANIMEXTS1.0",
+ 11)) { // Not everybody recognizes ANIMEXTS1.0 but it is valid
+ io->read_proc(&b, 1, 1, handle);
+ if (b == 3) { // we're supposed to have a 3 byte sub-block now
+ io->read_proc(&b, 1, 1,
+ handle); // this should be 0x01 but isn't really important
+ io->read_proc(&w, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&w);
+#endif
+ loop = w;
+ if (loop > 0)
+ loop++;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Graphic Control Extension
+ if (info->graphic_control_extension_offsets[page] != 0) {
+ io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[page] + 1),
+ SEEK_SET);
+ io->read_proc(&packed, 1, 1, handle);
+ io->read_proc(&w, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&w);
+#endif
+ io->read_proc(&b, 1, 1, handle);
+ have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
+ disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
+
+ transparent_color = b;
+ if (have_transparent) {
+ int size = 1 << bpp;
+ if (transparent_color <= size) {
+ BYTE table[256];
+ memset(table, 0xFF, size);
+ table[transparent_color] = 0;
+ FreeImage_SetTransparencyTable(dib, table, size);
+ dib->m_TransparentPaletteIndex = b;
+ }
+ }
+ }
+ b = (BYTE)disposal_method;
+
+ delete stringtable;
+
+ } catch (const char *msg) {
+ if (dib != NULL) {
+ FreeImage_Unload(dib);
+ }
+ FreeImage_OutputMessageProc(s_format_id, msg, io);
+ return NULL;
+ }
+
+ return dib;
+}
+
+static void DLL_CALLCONV Close(void *data)
+{
+ if (data == NULL) {
+ return;
+ }
+ GIFinfo *info = (GIFinfo *)data;
+ delete info;
+}
+
+SLoadedTexture *SLoadedTexture::LoadGIF(ISeekableIOStream &inStream, bool inFlipY,
+ NVFoundationBase &inFnd,
+ qt3ds::render::NVRenderContextType renderContextType)
+{
+ Q_UNUSED(renderContextType)
+ FreeImageIO theIO(inFnd.getAllocator(), inFnd);
+ void *gifData = Open(&theIO, &inStream);
+ if (gifData) {
+ SLoadedTexture *retval = DoLoadGIF(&theIO, &inStream, 0, gifData);
+ Close(gifData);
+ if (retval)
+ retval->FreeImagePostProcess(inFlipY);
+ return retval;
+ }
+ return NULL;
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureHDR.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureHDR.cpp
new file mode 100644
index 0000000..defff29
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureHDR.cpp
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+// ==========================================================
+// Radiance RGBE .HDR Loader
+// Decodes Radiance RGBE HDR image into FP16 texture buffer.
+//
+// Implementation by
+// Parashar Krishnamachari (parashark@nvidia.com)
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "Qt3DSRenderLoadedTextureFreeImageCompat.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+
+typedef unsigned char RGBE[4];
+#define R 0
+#define G 1
+#define B 2
+#define E 3
+
+#define MINELEN 8 // minimum scanline length for encoding
+#define MAXELEN 0x7fff // maximum scanline length for encoding
+
+static int s_format_id;
+
+static float convertComponent(int exponent, int val)
+{
+ float v = val / (256.0f);
+ float d = powf(2.0f, (float)exponent - 128.0f);
+ return v * d;
+}
+
+static void decrunchScanlineOld(FreeImageIO *io, fi_handle handle, RGBE *scanline, int width)
+{
+ int i;
+ int rshift = 0;
+
+ while (width > 0) {
+ io->read_proc(scanline, 4, 1, handle);
+
+ // The older version of RLE encodes the length in the exponent.
+ // and marks a run with 1, 1, 1 in RGB. This is differentiated from
+ // a raw value of 1, 1, 1, by having a exponent of 0;
+ if (scanline[0][R] == 1 && scanline[0][G] == 1 && scanline[0][B] == 1) {
+ for (i = (scanline[0][E] << rshift); i > 0; --i) {
+ memcpy(&scanline[0][0], &scanline[-1][0], 4);
+ ++scanline;
+ --width;
+ }
+ rshift += 8;
+ } else {
+ ++scanline;
+ --width;
+ rshift = 0;
+ }
+ }
+}
+
+static void decrunchScanline(FreeImageIO *io, fi_handle handle, RGBE *scanline, int width)
+{
+ if ((width < MINELEN) || (width > MAXELEN)) {
+ decrunchScanlineOld(io, handle, scanline, width);
+ return;
+ }
+
+ char c;
+ io->read_proc(&c, 1, 1, handle);
+ if (c != 2) {
+ io->seek_proc(handle, -1, SEEK_CUR);
+ decrunchScanlineOld(io, handle, scanline, width);
+ return;
+ }
+
+ io->read_proc(&(scanline[0][G]), 1, 1, handle);
+ io->read_proc(&(scanline[0][B]), 1, 1, handle);
+ io->read_proc(&c, 1, 1, handle);
+
+ if (scanline[0][G] != 2 || scanline[0][B] & 128) {
+ scanline[0][R] = 2;
+ scanline[0][E] = c;
+ decrunchScanlineOld(io, handle, scanline + 1, width - 1);
+ }
+
+ // RLE-encoded version does a separate buffer for each channel per scanline
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < width;) {
+ unsigned char code, val;
+ io->read_proc(&code, 1, 1, handle);
+ if (code
+ > 128) // RLE-encoded run... read 1 value and copy it forward for some n count.
+ {
+ code &= 127;
+ io->read_proc(&val, 1, 1, handle);
+ while (code--)
+ scanline[j++][i] = val;
+ } else // Not a run, so we read it as raw data
+ {
+ // Note -- we store each pixel in memory 4 bytes apart, so we can't just
+ // do one long read.
+ while (code--)
+ io->read_proc(&(scanline[j++][i]), 1, 1, handle);
+ }
+ }
+ }
+}
+
+static void decodeScanlineToTexture(RGBE *scanline, int width, void *outBuf, QT3DSU32 offset,
+ NVRenderTextureFormats::Enum inFormat)
+{
+ float rgbaF32[4];
+
+ for (int i = 0; i < width; ++i) {
+ rgbaF32[R] = convertComponent(scanline[i][E], scanline[i][R]);
+ rgbaF32[G] = convertComponent(scanline[i][E], scanline[i][G]);
+ rgbaF32[B] = convertComponent(scanline[i][E], scanline[i][B]);
+ rgbaF32[3] = 1.0f;
+
+ QT3DSU8 *target = reinterpret_cast<QT3DSU8 *>(outBuf);
+ target += offset;
+ NVRenderTextureFormats::encodeToPixel(
+ rgbaF32, target, i * NVRenderTextureFormats::getSizeofFormat(inFormat), inFormat);
+ }
+}
+
+static FIBITMAP *DoLoadHDR(FreeImageIO *io, fi_handle handle,
+ NVRenderTextureFormats::Enum inFormat = NVRenderTextureFormats::RGB32F)
+{
+ FIBITMAP *dib = NULL;
+ try {
+ if (handle != NULL) {
+ char str[200];
+ int i;
+
+ // Make sure it's a Radiance RGBE file
+ io->read_proc(str, 10, 1, handle);
+ if (memcmp(str, "#?RADIANCE", 10)) {
+ throw "Invalid HDR file";
+ }
+
+ io->seek_proc(handle, 1, SEEK_CUR);
+
+ // Get the command string (it's not really important for us; We're always assuming
+ // 32bit_rle_rgbe is the format
+ // we're just reading it to skip ahead the correct number of bytes).
+ i = 0;
+ char c = 0, prevC;
+ do {
+ prevC = c;
+ io->read_proc(&c, 1, 1, handle);
+ str[i++] = c;
+ } while (!(c == 0xa && prevC == 0xa));
+
+ // Get the resolution string (it will be NULL-terminated for us)
+ char res[200];
+ i = 0;
+ do {
+ io->read_proc(&c, 1, 1, handle);
+ res[i++] = c;
+ } while (c != 0xa);
+ res[i] = 0;
+
+ int width, height;
+ if (!sscanf(res, "-Y %d +X %d", &height, &width)) {
+ throw "Error encountered while loading HDR stream : could not determine image "
+ "resolution!";
+ }
+ int bytesPerPixel = NVRenderTextureFormats::getSizeofFormat(inFormat);
+ dib = FreeImage_Allocate(width, height, bytesPerPixel * 8, io);
+ if (dib == NULL) {
+ throw "DIB allocation failed";
+ }
+
+ dib->format = inFormat;
+ dib->components = NVRenderTextureFormats::getNumberOfComponent(inFormat);
+
+ // Allocate a scanline worth of RGBE data
+ RGBE *scanline = new RGBE[width];
+ if (!scanline) {
+ throw "Error encountered while loading HDR stream : could not buffer scanlines!";
+ }
+
+ // Go through all the scanlines
+ for (int y = 0; y < height; ++y) {
+ QT3DSU32 byteOfs = (height - 1 - y) * width * bytesPerPixel;
+ decrunchScanline(io, handle, scanline, width);
+ decodeScanlineToTexture(scanline, width, dib->data, byteOfs, inFormat);
+ }
+ }
+ return dib;
+ } catch (const char *message) {
+ if (dib) {
+ FreeImage_Unload(dib);
+ }
+ if (message) {
+ FreeImage_OutputMessageProc(s_format_id, message, io);
+ }
+ }
+
+ return NULL;
+}
+
+SLoadedTexture *SLoadedTexture::LoadHDR(ISeekableIOStream &inStream, NVFoundationBase &inFnd,
+ qt3ds::render::NVRenderContextType renderContextType)
+{
+ FreeImageIO theIO(inFnd.getAllocator(), inFnd);
+ SLoadedTexture *retval = nullptr;
+ if (renderContextType == qt3ds::render::NVRenderContextValues::GLES2)
+ retval = DoLoadHDR(&theIO, &inStream, NVRenderTextureFormats::RGBA8);
+ else
+ retval = DoLoadHDR(&theIO, &inStream, NVRenderTextureFormats::RGBA16F);
+
+
+ // Let's just assume we don't support this just yet.
+ // if ( retval )
+ // retval->FreeImagePostProcess( inFlipY );
+ return retval;
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.cpp
new file mode 100644
index 0000000..b1d4b05
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "Qt3DSRenderLoadedTexture.h"
+#include "Qt3DSRenderLoadedTextureKTX.h"
+#include "Qt3DSRenderLoadedTextureDDS.h"
+
+#include <qendian.h>
+#include <qopengltexture.h>
+
+using namespace qt3ds::render;
+using namespace qt3ds::foundation;
+
+namespace qt3ds {
+namespace render {
+
+static inline int blockSizeForTextureFormat(int format)
+{
+ switch (format) {
+ case QOpenGLTexture::RGB8_ETC1:
+ case QOpenGLTexture::RGB8_ETC2:
+ case QOpenGLTexture::SRGB8_ETC2:
+ case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
+ case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2:
+ case QOpenGLTexture::R11_EAC_UNorm:
+ case QOpenGLTexture::R11_EAC_SNorm:
+ case QOpenGLTexture::RGB_DXT1:
+ return 8;
+
+ default:
+ return 16;
+ }
+}
+
+static inline int runtimeFormat(quint32 internalFormat)
+{
+ switch (internalFormat) {
+ case QOpenGLTexture::RGB8_ETC1:
+ return NVRenderTextureFormats::RGB8_ETC1;
+ case QOpenGLTexture::RGB8_ETC2:
+ return NVRenderTextureFormats::RGB8_ETC2;
+ case QOpenGLTexture::SRGB8_ETC2:
+ return NVRenderTextureFormats::SRGB8_ETC2;
+ case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
+ return NVRenderTextureFormats::RGB8_PunchThrough_Alpha1_ETC2;
+ case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2:
+ return NVRenderTextureFormats::SRGB8_PunchThrough_Alpha1_ETC2;
+ case QOpenGLTexture::R11_EAC_UNorm:
+ return NVRenderTextureFormats::R11_EAC_UNorm;
+ case QOpenGLTexture::R11_EAC_SNorm:
+ return NVRenderTextureFormats::R11_EAC_SNorm;
+ case QOpenGLTexture::RGB_DXT1:
+ return NVRenderTextureFormats::RGB_DXT1;
+ case QOpenGLTexture::RGBA_DXT1:
+ return NVRenderTextureFormats::RGBA_DXT3;
+ case QOpenGLTexture::RGBA_DXT3:
+ return NVRenderTextureFormats::RGBA_DXT3;
+ case QOpenGLTexture::RGBA_DXT5:
+ return NVRenderTextureFormats::RGBA_DXT5;
+ default:
+ break;
+ }
+ return NVRenderTextureFormats::Unknown;
+}
+
+static inline int imageSize(QT3DSI32 width, QT3DSI32 height, const Qt3DSDDSImage *image)
+{
+ return ((width + 3) / 4) * ((height + 3) / 4)
+ * blockSizeForTextureFormat(image->internalFormat);
+}
+
+static inline quint32 totalImageDataSize(Qt3DSDDSImage *image)
+{
+ int i, j;
+ int index = 0;
+ quint32 size = 0;
+ int w, h;
+ int cubeCount = image->cubemap ? 6 : 1;
+
+ for (j = 0; j < cubeCount; j++) {
+ w = image->width;
+ h = image->height;
+
+ for (i = 0; i < image->numMipmaps; i++) // account for base plus each mip
+ {
+ size += 4; // image size is saved in the file
+ image->size[index] = imageSize(w, h, image);
+ image->mipwidth[index] = w;
+ image->mipheight[index] = h;
+ size += quint32(image->size[index]);
+ if (w != 1)
+ w >>= 1;
+ if (h != 1)
+ h >>= 1;
+
+ index++;
+ }
+ }
+
+ return (size);
+}
+
+static inline quint32 alignedOffset(quint32 offset, quint32 byteAlign)
+{
+ return (offset + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+inline SLoadedTexture *loadKtx(NVAllocatorCallback &allocator, IInStream &inStream,
+ QT3DSI32 flipVertical)
+{
+ static const int KTX_IDENTIFIER_LENGTH = 12;
+ static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = {
+ '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n'
+ };
+ static const quint32 platformEndianIdentifier = 0x04030201;
+ static const quint32 inversePlatformEndianIdentifier = 0x01020304;
+
+ struct KTXHeader {
+ quint8 identifier[KTX_IDENTIFIER_LENGTH];
+ quint32 endianness;
+ quint32 glType;
+ quint32 glTypeSize;
+ quint32 glFormat;
+ quint32 glInternalFormat;
+ quint32 glBaseInternalFormat;
+ quint32 pixelWidth;
+ quint32 pixelHeight;
+ quint32 pixelDepth;
+ quint32 numberOfArrayElements;
+ quint32 numberOfFaces;
+ quint32 numberOfMipmapLevels;
+ quint32 bytesOfKeyValueData;
+ };
+
+ KTXHeader header;
+ if (inStream.Read(header) != sizeof(header)
+ || qstrncmp(reinterpret_cast<char *>(header.identifier),
+ ktxIdentifier, KTX_IDENTIFIER_LENGTH) != 0
+ || (header.endianness != platformEndianIdentifier
+ && header.endianness != inversePlatformEndianIdentifier)) {
+ return nullptr;
+ }
+
+ const bool isInverseEndian = (header.endianness == inversePlatformEndianIdentifier);
+ auto decode = [isInverseEndian](quint32 val) {
+ return isInverseEndian ? qbswap<quint32>(val) : val;
+ };
+
+ const bool isCompressed = decode(header.glType) == 0 && decode(header.glFormat) == 0
+ && decode(header.glTypeSize) == 1;
+ if (!isCompressed) {
+ qWarning("Uncompressed ktx texture data is not supported");
+ return nullptr;
+ }
+
+ if (decode(header.numberOfArrayElements) != 0) {
+ qWarning("Array ktx textures not supported");
+ return nullptr;
+ }
+
+ if (decode(header.pixelDepth) != 0) {
+ qWarning("Only 2D and cube ktx textures are supported");
+ return nullptr;
+ }
+
+ const int bytesToSkip = int(decode(header.bytesOfKeyValueData));
+ QVector<uint8_t> skipData;
+ skipData.resize(bytesToSkip);
+ if (inStream.Read(NVDataRef<uint8_t>(skipData.data(), bytesToSkip)) != bytesToSkip) {
+ qWarning("Unexpected end of ktx data");
+ return nullptr;
+ }
+
+ // now for each mipmap level we have (arrays and 3d textures not supported here)
+ // uint32 imageSize
+ // for each array element
+ // for each face
+ // for each z slice
+ // compressed data
+ // padding so that each face data starts at an offset that is a multiple of 4
+ // padding so that each imageSize starts at an offset that is a multiple of 4
+
+ Qt3DSDDSImage *image = (Qt3DSDDSImage *)QT3DS_ALLOC(allocator, sizeof(Qt3DSDDSImage), "DoLoadDDS");
+
+ const quint32 level0Width = decode(header.pixelWidth);
+ const quint32 level0Height = decode(header.pixelHeight);
+ quint32 faceCount = decode(header.numberOfFaces);
+ const quint32 mipMapLevels = decode(header.numberOfMipmapLevels);
+ const quint32 format = decode(header.glInternalFormat);
+ image->numMipmaps = int(mipMapLevels);
+ image->cubemap = faceCount == 6 ? 6 : 0;
+ image->internalFormat = int(format);
+ image->format = runtimeFormat(format);
+ image->width = int(level0Width);
+ image->height = int(level0Height);
+ image->compressed = 1;
+ quint32 totalSize = totalImageDataSize(image);
+ image->dataBlock = QT3DS_ALLOC(allocator, totalSize, "Qt3DSDDSAllocDataBlock");
+ if (inStream.Read(NVDataRef<uint8_t>(reinterpret_cast<uint8_t*>(image->dataBlock), totalSize))
+ != totalSize) {
+ QT3DS_FREE(allocator, image);
+ return nullptr;
+ }
+
+ SLoadedTexture *result = QT3DS_NEW(allocator, SLoadedTexture)(allocator);
+ result->dds = image;
+ result->width = int(level0Width);
+ result->height = int(level0Height);
+ result->format = static_cast<NVRenderTextureFormats::Enum>(image->format);
+
+ // TODO: Proper support for cubemaps should be implemented at some point.
+ if (faceCount > 1) {
+ qWarning("Multiple faces (cubemaps) not currently supported in ktx");
+ faceCount = 1;
+ }
+
+ uint8_t *p = reinterpret_cast<uint8_t *>(image->dataBlock);
+ uint8_t *basep = p;
+
+ for (quint32 mip = 0; mip < mipMapLevels; ++mip) {
+ if (p + 4 - basep > totalSize)
+ break;
+ const quint32 imageSize = *reinterpret_cast<const quint32 *>(p);
+ p += 4;
+ for (quint32 face = 0; face < faceCount; ++face) {
+ const quint32 nextOffset = quint32(p + imageSize - basep);
+ if (nextOffset > totalSize)
+ break;
+ image->data[mip] = reinterpret_cast<void *>(p);
+ p = basep + alignedOffset(nextOffset, 4);
+ }
+ }
+
+ return result;
+}
+
+SLoadedTexture *SLoadedTexture::LoadKTX(IInStream &inStream, QT3DSI32 flipVertical,
+ NVFoundationBase &inFnd,
+ qt3ds::render::NVRenderContextType renderContextType)
+{
+ Q_UNUSED(renderContextType)
+ SLoadedTexture *retval = loadKtx(inFnd.getAllocator(), inStream, flipVertical);
+
+ return retval;
+}
+
+}
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.h b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.h
new file mode 100644
index 0000000..3a2d762
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureKTX.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DS_RENDER_LOAD_KTX_H
+#define QT3DS_RENDER_LOAD_KTX_H
+
+namespace qt3ds {
+namespace render {
+
+}
+}
+
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.cpp
new file mode 100644
index 0000000..023964f
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.cpp
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSRenderPrefilterTexture.h"
+#include "render/Qt3DSRenderContext.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+
+#include <string>
+
+using namespace qt3ds;
+using namespace qt3ds::render;
+using namespace qt3ds::foundation;
+
+Qt3DSRenderPrefilterTexture::Qt3DSRenderPrefilterTexture(NVRenderContext *inNVRenderContext,
+ QT3DSI32 inWidth, QT3DSI32 inHeight,
+ NVRenderTexture2D &inTexture2D,
+ NVRenderTextureFormats::Enum inDestFormat,
+ NVFoundationBase &inFnd)
+ : m_Foundation(inFnd)
+ , mRefCount(0)
+ , m_Texture2D(inTexture2D)
+ , m_DestinationFormat(inDestFormat)
+ , m_Width(inWidth)
+ , m_Height(inHeight)
+ , m_NVRenderContext(inNVRenderContext)
+{
+ // Calculate mip level
+ int maxDim = inWidth >= inHeight ? inWidth : inHeight;
+
+ m_MaxMipMapLevel = static_cast<int>(logf((float)maxDim) / logf(2.0f));
+ // no concept of sizeOfFormat just does'nt make sense
+ m_SizeOfFormat = NVRenderTextureFormats::getSizeofFormat(m_DestinationFormat);
+ m_NoOfComponent = NVRenderTextureFormats::getNumberOfComponent(m_DestinationFormat);
+}
+
+Qt3DSRenderPrefilterTexture *
+Qt3DSRenderPrefilterTexture::Create(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth, QT3DSI32 inHeight,
+ NVRenderTexture2D &inTexture2D,
+ NVRenderTextureFormats::Enum inDestFormat,
+ qt3ds::NVFoundationBase &inFnd)
+{
+ Qt3DSRenderPrefilterTexture *theBSDFMipMap = NULL;
+
+ if (inNVRenderContext->IsComputeSupported()) {
+ theBSDFMipMap = QT3DS_NEW(inFnd.getAllocator(), Qt3DSRenderPrefilterTextureCompute)(
+ inNVRenderContext, inWidth, inHeight, inTexture2D, inDestFormat, inFnd);
+ }
+
+ if (!theBSDFMipMap) {
+ theBSDFMipMap = QT3DS_NEW(inFnd.getAllocator(), Qt3DSRenderPrefilterTextureCPU)(
+ inNVRenderContext, inWidth, inHeight, inTexture2D, inDestFormat, inFnd);
+ }
+
+ if (theBSDFMipMap)
+ theBSDFMipMap->addRef();
+
+ return theBSDFMipMap;
+}
+
+Qt3DSRenderPrefilterTexture::~Qt3DSRenderPrefilterTexture()
+{
+}
+
+//------------------------------------------------------------------------------------
+// CPU based filtering
+//------------------------------------------------------------------------------------
+
+Qt3DSRenderPrefilterTextureCPU::Qt3DSRenderPrefilterTextureCPU(
+ NVRenderContext *inNVRenderContext, int inWidth, int inHeight, NVRenderTexture2D &inTexture2D,
+ NVRenderTextureFormats::Enum inDestFormat, NVFoundationBase &inFnd)
+ : Qt3DSRenderPrefilterTexture(inNVRenderContext, inWidth, inHeight, inTexture2D, inDestFormat,
+ inFnd)
+{
+}
+
+inline int Qt3DSRenderPrefilterTextureCPU::wrapMod(int a, int base)
+{
+ return (a >= 0) ? a % base : (a % base) + base;
+}
+
+inline void Qt3DSRenderPrefilterTextureCPU::getWrappedCoords(int &sX, int &sY, int width, int height)
+{
+ if (sY < 0) {
+ sX -= width >> 1;
+ sY = -sY;
+ }
+ if (sY >= height) {
+ sX += width >> 1;
+ sY = height - sY;
+ }
+ sX = wrapMod(sX, width);
+}
+
+STextureData
+Qt3DSRenderPrefilterTextureCPU::CreateBsdfMipLevel(STextureData &inCurMipLevel,
+ STextureData &inPrevMipLevel, int width,
+ int height) //, IPerfTimer& inPerfTimer )
+{
+ STextureData retval;
+ int newWidth = width >> 1;
+ int newHeight = height >> 1;
+ newWidth = newWidth >= 1 ? newWidth : 1;
+ newHeight = newHeight >= 1 ? newHeight : 1;
+
+ if (inCurMipLevel.data) {
+ retval = inCurMipLevel;
+ retval.dataSizeInBytes =
+ newWidth * newHeight * NVRenderTextureFormats::getSizeofFormat(inPrevMipLevel.format);
+ } else {
+ retval.dataSizeInBytes =
+ newWidth * newHeight * NVRenderTextureFormats::getSizeofFormat(inPrevMipLevel.format);
+ retval.format = inPrevMipLevel.format; // inLoadedImage.format;
+ retval.data = m_Foundation.getAllocator().allocate(
+ retval.dataSizeInBytes, "Bsdf Scaled Image Data", __FILE__, __LINE__);
+ }
+
+ for (int y = 0; y < newHeight; ++y) {
+ for (int x = 0; x < newWidth; ++x) {
+ float accumVal[4];
+ accumVal[0] = 0;
+ accumVal[1] = 0;
+ accumVal[2] = 0;
+ accumVal[3] = 0;
+ for (int sy = -2; sy <= 2; ++sy) {
+ for (int sx = -2; sx <= 2; ++sx) {
+ int sampleX = sx + (x << 1);
+ int sampleY = sy + (y << 1);
+ getWrappedCoords(sampleX, sampleY, width, height);
+
+ // Cauchy filter (this is simply because it's the easiest to evaluate, and
+ // requires no complex
+ // functions).
+ float filterPdf = 1.f / (1.f + float(sx * sx + sy * sy) * 2.f);
+ // With FP HDR formats, we're not worried about intensity loss so much as
+ // unnecessary energy gain,
+ // whereas with LDR formats, the fear with a continuous normalization factor is
+ // that we'd lose
+ // intensity and saturation as well.
+ filterPdf /= (NVRenderTextureFormats::getSizeofFormat(retval.format) >= 8)
+ ? 4.71238898f
+ : 4.5403446f;
+ // filterPdf /= 4.5403446f; // Discrete normalization factor
+ // filterPdf /= 4.71238898f; // Continuous normalization factor
+ float curPix[4];
+ QT3DSI32 byteOffset = (sampleY * width + sampleX)
+ * NVRenderTextureFormats::getSizeofFormat(retval.format);
+ if (byteOffset < 0) {
+ sampleY = height + sampleY;
+ byteOffset = (sampleY * width + sampleX)
+ * NVRenderTextureFormats::getSizeofFormat(retval.format);
+ }
+
+ NVRenderTextureFormats::decodeToFloat(inPrevMipLevel.data, byteOffset, curPix,
+ retval.format);
+
+ accumVal[0] += filterPdf * curPix[0];
+ accumVal[1] += filterPdf * curPix[1];
+ accumVal[2] += filterPdf * curPix[2];
+ accumVal[3] += filterPdf * curPix[3];
+ }
+ }
+
+ QT3DSU32 newIdx =
+ (y * newWidth + x) * NVRenderTextureFormats::getSizeofFormat(retval.format);
+
+ NVRenderTextureFormats::encodeToPixel(accumVal, retval.data, newIdx, retval.format);
+ }
+ }
+
+ return retval;
+}
+
+void Qt3DSRenderPrefilterTextureCPU::Build(void *inTextureData, QT3DSI32 inTextureDataSize,
+ NVRenderTextureFormats::Enum inFormat)
+{
+
+ m_InternalFormat = inFormat;
+ m_SizeOfInternalFormat = NVRenderTextureFormats::getSizeofFormat(m_InternalFormat);
+ m_InternalNoOfComponent = NVRenderTextureFormats::getNumberOfComponent(m_InternalFormat);
+
+ m_Texture2D.SetTextureData(NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize), 0,
+ m_Width, m_Height, inFormat, m_DestinationFormat);
+
+ STextureData theMipImage;
+ STextureData prevImage;
+ prevImage.data = inTextureData;
+ prevImage.dataSizeInBytes = inTextureDataSize;
+ prevImage.format = inFormat;
+ int curWidth = m_Width;
+ int curHeight = m_Height;
+ int size = NVRenderTextureFormats::getSizeofFormat(m_InternalFormat);
+ for (int idx = 1; idx <= m_MaxMipMapLevel; ++idx) {
+ theMipImage =
+ CreateBsdfMipLevel(theMipImage, prevImage, curWidth, curHeight); //, m_PerfTimer );
+ curWidth = curWidth >> 1;
+ curHeight = curHeight >> 1;
+ curWidth = curWidth >= 1 ? curWidth : 1;
+ curHeight = curHeight >= 1 ? curHeight : 1;
+ inTextureDataSize = curWidth * curHeight * size;
+
+ m_Texture2D.SetTextureData(toU8DataRef((char *)theMipImage.data, (QT3DSU32)inTextureDataSize),
+ (QT3DSU8)idx, (QT3DSU32)curWidth, (QT3DSU32)curHeight, theMipImage.format,
+ m_DestinationFormat);
+
+ if (prevImage.data == inTextureData)
+ prevImage = STextureData();
+
+ STextureData temp = prevImage;
+ prevImage = theMipImage;
+ theMipImage = temp;
+ }
+ QT3DS_FREE(m_Foundation.getAllocator(), theMipImage.data);
+ QT3DS_FREE(m_Foundation.getAllocator(), prevImage.data);
+}
+
+//------------------------------------------------------------------------------------
+// GL compute based filtering
+//------------------------------------------------------------------------------------
+
+static const char *computeUploadShader(std::string &prog, NVRenderTextureFormats::Enum inFormat,
+ bool binESContext)
+{
+ if (binESContext) {
+ prog += "#version 310 es\n"
+ "#extension GL_ARB_compute_shader : enable\n"
+ "precision highp float;\n"
+ "precision highp int;\n"
+ "precision mediump image2D;\n";
+ } else {
+ prog += "#version 430\n"
+ "#extension GL_ARB_compute_shader : enable\n";
+ }
+
+ if (inFormat == NVRenderTextureFormats::RGBA8) {
+ prog += "// Set workgroup layout;\n"
+ "layout (local_size_x = 16, local_size_y = 16) in;\n\n"
+ "layout (rgba8, binding = 1) readonly uniform image2D inputImage;\n\n"
+ "layout (rgba16f, binding = 2) writeonly uniform image2D outputImage;\n\n"
+ "void main()\n"
+ "{\n"
+ " if ( gl_GlobalInvocationID.x >= gl_NumWorkGroups.x || gl_GlobalInvocationID.y "
+ ">= gl_NumWorkGroups.y )\n"
+ " return;\n"
+ " vec4 value = imageLoad(inputImage, ivec2(gl_GlobalInvocationID.xy));\n"
+ " imageStore( outputImage, ivec2(gl_GlobalInvocationID.xy), value );\n"
+ "}\n";
+ } else {
+ prog += "float convertToFloat( in uint inValue )\n"
+ "{\n"
+ " uint v = inValue & uint(0xFF);\n"
+ " float f = float(v)/256.0;\n"
+ " return f;\n"
+ "}\n";
+
+ prog += "int getMod( in int inValue, in int mod )\n"
+ "{\n"
+ " int v = mod * (inValue/mod);\n"
+ " return inValue - v;\n"
+ "}\n";
+
+ prog += "vec4 getRGBValue( in int byteNo, vec4 inVal, vec4 inVal1 )\n"
+ "{\n"
+ " vec4 result= vec4(0.0);\n"
+ " if( byteNo == 0) {\n"
+ " result.r = inVal.r;\n"
+ " result.g = inVal.g;\n"
+ " result.b = inVal.b;\n"
+ " }\n"
+ " else if( byteNo == 1) {\n"
+ " result.r = inVal.g;\n"
+ " result.g = inVal.b;\n"
+ " result.b = inVal.a;\n"
+ " }\n"
+ " else if( byteNo == 2) {\n"
+ " result.r = inVal.b;\n"
+ " result.g = inVal.a;\n"
+ " result.b = inVal1.r;\n"
+ " }\n"
+ " else if( byteNo == 3) {\n"
+ " result.r = inVal.a;\n"
+ " result.g = inVal1.r;\n"
+ " result.b = inVal1.g;\n"
+ " }\n"
+ " return result;\n"
+ "}\n";
+
+ prog += "// Set workgroup layout;\n"
+ "layout (local_size_x = 16, local_size_y = 16) in;\n\n"
+ "layout (rgba8, binding = 1) readonly uniform image2D inputImage;\n\n"
+ "layout (rgba16f, binding = 2) writeonly uniform image2D outputImage;\n\n"
+ "void main()\n"
+ "{\n"
+ " vec4 result = vec4(0.0);\n"
+ " if ( gl_GlobalInvocationID.x >= gl_NumWorkGroups.x || gl_GlobalInvocationID.y "
+ ">= gl_NumWorkGroups.y )\n"
+ " return;\n"
+ " int xpos = (int(gl_GlobalInvocationID.x)*3)/4;\n"
+ " int xmod = getMod(int(gl_GlobalInvocationID.x)*3, 4);\n"
+ " ivec2 readPos = ivec2(xpos, gl_GlobalInvocationID.y);\n"
+ " vec4 value = imageLoad(inputImage, readPos);\n"
+ " vec4 value1 = imageLoad(inputImage, ivec2(readPos.x + 1, readPos.y));\n"
+ " result = getRGBValue( xmod, value, value1);\n"
+ " imageStore( outputImage, ivec2(gl_GlobalInvocationID.xy), result );\n"
+ "}\n";
+ }
+ return prog.c_str();
+}
+
+static const char *computeWorkShader(std::string &prog, bool binESContext)
+{
+ if (binESContext) {
+ prog += "#version 310 es\n"
+ "#extension GL_ARB_compute_shader : enable\n"
+ "precision highp float;\n"
+ "precision highp int;\n"
+ "precision mediump image2D;\n";
+ } else {
+ prog += "#version 430\n"
+ "#extension GL_ARB_compute_shader : enable\n";
+ }
+
+ prog += "int wrapMod( in int a, in int base )\n"
+ "{\n"
+ " return ( a >= 0 ) ? a % base : -(a % base) + base;\n"
+ "}\n";
+
+ prog += "void getWrappedCoords( inout int sX, inout int sY, in int width, in int height )\n"
+ "{\n"
+ " if (sY < 0) { sX -= width >> 1; sY = -sY; }\n"
+ " if (sY >= height) { sX += width >> 1; sY = height - sY; }\n"
+ " sX = wrapMod( sX, width );\n"
+ "}\n";
+
+ prog += "// Set workgroup layout;\n"
+ "layout (local_size_x = 16, local_size_y = 16) in;\n\n"
+ "layout (rgba16f, binding = 1) readonly uniform image2D inputImage;\n\n"
+ "layout (rgba16f, binding = 2) writeonly uniform image2D outputImage;\n\n"
+ "void main()\n"
+ "{\n"
+ " int prevWidth = int(gl_NumWorkGroups.x) << 1;\n"
+ " int prevHeight = int(gl_NumWorkGroups.y) << 1;\n"
+ " if ( gl_GlobalInvocationID.x >= gl_NumWorkGroups.x || gl_GlobalInvocationID.y >= "
+ "gl_NumWorkGroups.y )\n"
+ " return;\n"
+ " vec4 accumVal = vec4(0.0);\n"
+ " for ( int sy = -2; sy <= 2; ++sy )\n"
+ " {\n"
+ " for ( int sx = -2; sx <= 2; ++sx )\n"
+ " {\n"
+ " int sampleX = sx + (int(gl_GlobalInvocationID.x) << 1);\n"
+ " int sampleY = sy + (int(gl_GlobalInvocationID.y) << 1);\n"
+ " getWrappedCoords(sampleX, sampleY, prevWidth, prevHeight);\n"
+ " if ((sampleY * prevWidth + sampleX) < 0 )\n"
+ " sampleY = prevHeight + sampleY;\n"
+ " ivec2 pos = ivec2(sampleX, sampleY);\n"
+ " vec4 value = imageLoad(inputImage, pos);\n"
+ " float filterPdf = 1.0 / ( 1.0 + float(sx*sx + sy*sy)*2.0 );\n"
+ " filterPdf /= 4.71238898;\n"
+ " accumVal[0] += filterPdf * value.r;\n"
+ " accumVal[1] += filterPdf * value.g;\n"
+ " accumVal[2] += filterPdf * value.b;\n"
+ " accumVal[3] += filterPdf * value.a;\n"
+ " }\n"
+ " }\n"
+ " imageStore( outputImage, ivec2(gl_GlobalInvocationID.xy), accumVal );\n"
+ "}\n";
+
+ return prog.c_str();
+}
+
+inline NVConstDataRef<QT3DSI8> toRef(const char *data)
+{
+ size_t len = strlen(data) + 1;
+ return NVConstDataRef<QT3DSI8>((const QT3DSI8 *)data, (QT3DSU32)len);
+}
+
+static bool isGLESContext(NVRenderContext *context)
+{
+ NVRenderContextType ctxType = context->GetRenderContextType();
+
+ // Need minimum of GL3 or GLES3
+ if (ctxType == NVRenderContextValues::GLES2 || ctxType == NVRenderContextValues::GLES3
+ || ctxType == NVRenderContextValues::GLES3PLUS) {
+ return true;
+ }
+
+ return false;
+}
+
+#define WORKGROUP_SIZE 16
+
+Qt3DSRenderPrefilterTextureCompute::Qt3DSRenderPrefilterTextureCompute(
+ NVRenderContext *inNVRenderContext, QT3DSI32 inWidth, QT3DSI32 inHeight,
+ NVRenderTexture2D &inTexture2D, NVRenderTextureFormats::Enum inDestFormat,
+ NVFoundationBase &inFnd)
+ : Qt3DSRenderPrefilterTexture(inNVRenderContext, inWidth, inHeight, inTexture2D, inDestFormat,
+ inFnd)
+ , m_BSDFProgram(NULL)
+ , m_UploadProgram_RGBA8(NULL)
+ , m_UploadProgram_RGB8(NULL)
+ , m_Level0Tex(NULL)
+ , m_TextureCreated(false)
+{
+}
+
+Qt3DSRenderPrefilterTextureCompute::~Qt3DSRenderPrefilterTextureCompute()
+{
+ m_UploadProgram_RGB8 = NULL;
+ m_UploadProgram_RGBA8 = NULL;
+ m_BSDFProgram = NULL;
+ m_Level0Tex = NULL;
+}
+
+void Qt3DSRenderPrefilterTextureCompute::createComputeProgram(NVRenderContext *context)
+{
+ std::string computeProg;
+
+ if (!m_BSDFProgram) {
+ m_BSDFProgram = context
+ ->CompileComputeSource(
+ "Compute BSDF mipmap shader",
+ toRef(computeWorkShader(computeProg, isGLESContext(context))))
+ .mShader;
+ }
+}
+
+NVRenderShaderProgram *Qt3DSRenderPrefilterTextureCompute::getOrCreateUploadComputeProgram(
+ NVRenderContext *context, NVRenderTextureFormats::Enum inFormat)
+{
+ std::string computeProg;
+
+ if (inFormat == NVRenderTextureFormats::RGB8) {
+ if (!m_UploadProgram_RGB8) {
+ m_UploadProgram_RGB8 =
+ context
+ ->CompileComputeSource(
+ "Compute BSDF mipmap level 0 RGB8 shader",
+ toRef(computeUploadShader(computeProg, inFormat, isGLESContext(context))))
+ .mShader;
+ }
+
+ return m_UploadProgram_RGB8;
+ } else {
+ if (!m_UploadProgram_RGBA8) {
+ m_UploadProgram_RGBA8 =
+ context
+ ->CompileComputeSource(
+ "Compute BSDF mipmap level 0 RGBA8 shader",
+ toRef(computeUploadShader(computeProg, inFormat, isGLESContext(context))))
+ .mShader;
+ }
+
+ return m_UploadProgram_RGBA8;
+ }
+}
+
+void Qt3DSRenderPrefilterTextureCompute::CreateLevel0Tex(void *inTextureData, QT3DSI32 inTextureDataSize,
+ NVRenderTextureFormats::Enum inFormat)
+{
+ NVRenderTextureFormats::Enum theFormat = inFormat;
+ QT3DSI32 theWidth = m_Width;
+
+ // Since we cannot use RGB format in GL compute
+ // we treat it as a RGBA component format
+ if (inFormat == NVRenderTextureFormats::RGB8) {
+ // This works only with 4 byte aligned data
+ QT3DS_ASSERT(m_Width % 4 == 0);
+ theFormat = NVRenderTextureFormats::RGBA8;
+ theWidth = (m_Width * 3) / 4;
+ }
+
+ if (m_Level0Tex == NULL) {
+ m_Level0Tex = m_NVRenderContext->CreateTexture2D();
+ m_Level0Tex->SetTextureStorage(1, theWidth, m_Height, theFormat, theFormat,
+ NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize));
+ } else {
+ m_Level0Tex->SetTextureSubData(NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize), 0,
+ 0, 0, theWidth, m_Height, theFormat);
+ }
+}
+
+void Qt3DSRenderPrefilterTextureCompute::Build(void *inTextureData, QT3DSI32 inTextureDataSize,
+ NVRenderTextureFormats::Enum inFormat)
+{
+ bool needMipUpload = (inFormat != m_DestinationFormat);
+ // re-upload data
+ if (!m_TextureCreated) {
+ m_Texture2D.SetTextureStorage(
+ m_MaxMipMapLevel + 1, m_Width, m_Height, m_DestinationFormat, inFormat, (needMipUpload)
+ ? NVDataRef<QT3DSU8>()
+ : NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize));
+ m_Texture2D.addRef();
+ // create a compute shader (if not aloread done) which computes the BSDF mipmaps for this
+ // texture
+ createComputeProgram(m_NVRenderContext);
+
+ if (!m_BSDFProgram) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ m_TextureCreated = true;
+ } else if (!needMipUpload) {
+ m_Texture2D.SetTextureSubData(NVDataRef<QT3DSU8>((QT3DSU8 *)inTextureData, inTextureDataSize), 0,
+ 0, 0, m_Width, m_Height, inFormat);
+ }
+
+ if (needMipUpload) {
+ CreateLevel0Tex(inTextureData, inTextureDataSize, inFormat);
+ }
+
+ NVScopedRefCounted<NVRenderImage2D> theInputImage;
+ NVScopedRefCounted<NVRenderImage2D> theOutputImage;
+ theInputImage =
+ m_NVRenderContext->CreateImage2D(&m_Texture2D, NVRenderImageAccessType::ReadWrite);
+ theOutputImage =
+ m_NVRenderContext->CreateImage2D(&m_Texture2D, NVRenderImageAccessType::ReadWrite);
+
+ if (needMipUpload && m_Level0Tex) {
+ NVRenderShaderProgram *uploadProg =
+ getOrCreateUploadComputeProgram(m_NVRenderContext, inFormat);
+ if (!uploadProg)
+ return;
+
+ m_NVRenderContext->SetActiveShader(uploadProg);
+
+ NVScopedRefCounted<NVRenderImage2D> theInputImage0;
+ theInputImage0 =
+ m_NVRenderContext->CreateImage2D(m_Level0Tex, NVRenderImageAccessType::ReadWrite);
+
+ theInputImage0->SetTextureLevel(0);
+ NVRenderCachedShaderProperty<NVRenderImage2D *> theCachedinputImage0("inputImage",
+ *uploadProg);
+ theCachedinputImage0.Set(theInputImage0);
+
+ theOutputImage->SetTextureLevel(0);
+ NVRenderCachedShaderProperty<NVRenderImage2D *> theCachedOutputImage("outputImage",
+ *uploadProg);
+ theCachedOutputImage.Set(theOutputImage);
+
+ m_NVRenderContext->DispatchCompute(uploadProg, m_Width, m_Height, 1);
+
+ // sync
+ NVRenderBufferBarrierFlags flags(NVRenderBufferBarrierValues::ShaderImageAccess);
+ m_NVRenderContext->SetMemoryBarrier(flags);
+ }
+
+ int width = m_Width >> 1;
+ int height = m_Height >> 1;
+
+ m_NVRenderContext->SetActiveShader(m_BSDFProgram);
+
+ for (int i = 1; i <= m_MaxMipMapLevel; ++i) {
+ theOutputImage->SetTextureLevel(i);
+ NVRenderCachedShaderProperty<NVRenderImage2D *> theCachedOutputImage("outputImage",
+ *m_BSDFProgram);
+ theCachedOutputImage.Set(theOutputImage);
+ theInputImage->SetTextureLevel(i - 1);
+ NVRenderCachedShaderProperty<NVRenderImage2D *> theCachedinputImage("inputImage",
+ *m_BSDFProgram);
+ theCachedinputImage.Set(theInputImage);
+
+ m_NVRenderContext->DispatchCompute(m_BSDFProgram, width, height, 1);
+
+ width = width > 2 ? width >> 1 : 1;
+ height = height > 2 ? height >> 1 : 1;
+
+ // sync
+ NVRenderBufferBarrierFlags flags(NVRenderBufferBarrierValues::ShaderImageAccess);
+ m_NVRenderContext->SetMemoryBarrier(flags);
+ }
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.h b/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.h
new file mode 100644
index 0000000..e633eb1
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderPrefilterTexture.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_RENDER_PREFILTER_TEXTURE_H
+#define QT3DS_RENDER_PREFILTER_TEXTURE_H
+#include "foundation/Qt3DSAtomic.h"
+#include "render/Qt3DSRenderTexture2D.h"
+#include "Qt3DSRender.h"
+
+#include "Qt3DSTypes.h"
+#include "Qt3DSRenderLoadedTexture.h"
+
+namespace qt3ds {
+namespace render {
+
+ class Qt3DSRenderPrefilterTexture : public NVRefCounted
+ {
+ public:
+ Qt3DSRenderPrefilterTexture(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth, QT3DSI32 inHeight,
+ NVRenderTexture2D &inTexture,
+ NVRenderTextureFormats::Enum inDestFormat,
+ qt3ds::NVFoundationBase &inFnd);
+ virtual ~Qt3DSRenderPrefilterTexture();
+
+ virtual void Build(void *inTextureData, QT3DSI32 inTextureDataSize,
+ NVRenderTextureFormats::Enum inFormat) = 0;
+
+ static Qt3DSRenderPrefilterTexture *Create(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth,
+ QT3DSI32 inHeight, NVRenderTexture2D &inTexture,
+ NVRenderTextureFormats::Enum inDestFormat,
+ qt3ds::NVFoundationBase &inFnd);
+
+ protected:
+ NVFoundationBase &m_Foundation; ///< Foundation class for allocations and other base things
+ volatile QT3DSI32 mRefCount; ///< reference count
+
+ NVRenderTexture2D &m_Texture2D;
+ NVRenderTextureFormats::Enum m_InternalFormat;
+ NVRenderTextureFormats::Enum m_DestinationFormat;
+
+ QT3DSI32 m_Width;
+ QT3DSI32 m_Height;
+ QT3DSI32 m_MaxMipMapLevel;
+ QT3DSI32 m_SizeOfFormat;
+ QT3DSI32 m_SizeOfInternalFormat;
+ QT3DSI32 m_InternalNoOfComponent;
+ QT3DSI32 m_NoOfComponent;
+ NVRenderContext *m_NVRenderContext;
+ };
+
+ class Qt3DSRenderPrefilterTextureCPU : public Qt3DSRenderPrefilterTexture
+ {
+ public:
+ Qt3DSRenderPrefilterTextureCPU(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth,
+ QT3DSI32 inHeight, NVRenderTexture2D &inTexture,
+ NVRenderTextureFormats::Enum inDestFormat,
+ qt3ds::NVFoundationBase &inFnd);
+
+ void Build(void *inTextureData, QT3DSI32 inTextureDataSize,
+ NVRenderTextureFormats::Enum inFormat) override;
+
+ STextureData CreateBsdfMipLevel(STextureData &inCurMipLevel, STextureData &inPrevMipLevel,
+ QT3DSI32 width, QT3DSI32 height);
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation)
+
+ int wrapMod(int a, int base);
+ void getWrappedCoords(int &sX, int &sY, int width, int height);
+ };
+
+ class Qt3DSRenderPrefilterTextureCompute : public Qt3DSRenderPrefilterTexture
+ {
+ public:
+ Qt3DSRenderPrefilterTextureCompute(NVRenderContext *inNVRenderContext, QT3DSI32 inWidth,
+ QT3DSI32 inHeight, NVRenderTexture2D &inTexture,
+ NVRenderTextureFormats::Enum inDestFormat,
+ qt3ds::NVFoundationBase &inFnd);
+ ~Qt3DSRenderPrefilterTextureCompute();
+
+ void Build(void *inTextureData, QT3DSI32 inTextureDataSize,
+ NVRenderTextureFormats::Enum inFormat) override;
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation)
+
+ private:
+ void CreateLevel0Tex(void *inTextureData, QT3DSI32 inTextureDataSize,
+ NVRenderTextureFormats::Enum inFormat);
+
+ NVScopedRefCounted<NVRenderShaderProgram> m_BSDFProgram;
+ NVScopedRefCounted<NVRenderShaderProgram> m_UploadProgram_RGBA8;
+ NVScopedRefCounted<NVRenderShaderProgram> m_UploadProgram_RGB8;
+ NVScopedRefCounted<NVRenderTexture2D> m_Level0Tex;
+ bool m_TextureCreated;
+
+ void createComputeProgram(NVRenderContext *context);
+ NVRenderShaderProgram *
+ getOrCreateUploadComputeProgram(NVRenderContext *context,
+ NVRenderTextureFormats::Enum inFormat);
+ };
+}
+}
+
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.cpp
new file mode 100644
index 0000000..25fbb41
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderResourceBufferObjects.h"
+
+using namespace qt3ds::render;
+
+/*
+ IResourceManager& m_ResourceManager;
+ NVRenderFrameBuffer* m_FrameBuffer;
+ */
+
+CResourceFrameBuffer::CResourceFrameBuffer(IResourceManager &mgr)
+ : m_ResourceManager(mgr)
+ , m_FrameBuffer(NULL)
+{
+}
+
+CResourceFrameBuffer::~CResourceFrameBuffer()
+{
+ ReleaseFrameBuffer();
+}
+
+bool CResourceFrameBuffer::EnsureFrameBuffer()
+{
+ if (!m_FrameBuffer) {
+ m_FrameBuffer = m_ResourceManager.AllocateFrameBuffer();
+ return true;
+ }
+ return false;
+}
+
+void CResourceFrameBuffer::ReleaseFrameBuffer()
+{
+ if (m_FrameBuffer) {
+ m_ResourceManager.Release(*m_FrameBuffer);
+ }
+}
+
+CResourceRenderBuffer::CResourceRenderBuffer(IResourceManager &mgr)
+ : m_ResourceManager(mgr)
+ , m_RenderBuffer(NULL)
+{
+}
+
+CResourceRenderBuffer::~CResourceRenderBuffer()
+{
+ ReleaseRenderBuffer();
+}
+
+bool CResourceRenderBuffer::EnsureRenderBuffer(QT3DSU32 width, QT3DSU32 height,
+ NVRenderRenderBufferFormats::Enum storageFormat)
+{
+ if (m_RenderBuffer == NULL || m_Dimensions.m_Width != width || m_Dimensions.m_Height != height
+ || m_StorageFormat != storageFormat) {
+ if (m_RenderBuffer == NULL || m_StorageFormat != storageFormat) {
+ ReleaseRenderBuffer();
+ m_RenderBuffer = m_ResourceManager.AllocateRenderBuffer(width, height, storageFormat);
+ } else
+ m_RenderBuffer->SetDimensions(
+ qt3ds::render::NVRenderRenderBufferDimensions(width, height));
+ m_Dimensions = m_RenderBuffer->GetDimensions();
+ m_StorageFormat = m_RenderBuffer->GetStorageFormat();
+ return true;
+ }
+ return false;
+}
+
+void CResourceRenderBuffer::ReleaseRenderBuffer()
+{
+ if (m_RenderBuffer) {
+ m_ResourceManager.Release(*m_RenderBuffer);
+ m_RenderBuffer = NULL;
+ }
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.h b/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.h
new file mode 100644
index 0000000..fb54c4d
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceBufferObjects.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_RENDER_RESOURCE_BUFFER_OBJECTS_H
+#define QT3DS_RENDER_RESOURCE_BUFFER_OBJECTS_H
+#include "Qt3DSRender.h"
+#include "render/Qt3DSRenderContext.h"
+#include "Qt3DSRenderResourceManager.h"
+#include "render/Qt3DSRenderFrameBuffer.h"
+#include "render/Qt3DSRenderRenderBuffer.h"
+
+namespace qt3ds {
+namespace render {
+ class CResourceFrameBuffer
+ {
+ protected:
+ IResourceManager &m_ResourceManager;
+ NVRenderFrameBuffer *m_FrameBuffer;
+
+ public:
+ CResourceFrameBuffer(IResourceManager &mgr);
+ ~CResourceFrameBuffer();
+ bool EnsureFrameBuffer();
+ void ReleaseFrameBuffer();
+
+ IResourceManager &GetResourceManager() { return m_ResourceManager; }
+ operator NVRenderFrameBuffer *() { return m_FrameBuffer; }
+ NVRenderFrameBuffer *operator->()
+ {
+ QT3DS_ASSERT(m_FrameBuffer);
+ return m_FrameBuffer;
+ }
+ NVRenderFrameBuffer &operator*()
+ {
+ QT3DS_ASSERT(m_FrameBuffer);
+ return *m_FrameBuffer;
+ }
+ };
+
+ class CResourceRenderBuffer
+ {
+ protected:
+ IResourceManager &m_ResourceManager;
+ NVRenderRenderBuffer *m_RenderBuffer;
+ qt3ds::render::NVRenderRenderBufferFormats::Enum m_StorageFormat;
+ qt3ds::render::NVRenderRenderBufferDimensions m_Dimensions;
+
+ public:
+ CResourceRenderBuffer(IResourceManager &mgr);
+ ~CResourceRenderBuffer();
+ bool EnsureRenderBuffer(QT3DSU32 width, QT3DSU32 height,
+ NVRenderRenderBufferFormats::Enum storageFormat);
+ void ReleaseRenderBuffer();
+
+ operator NVRenderRenderBuffer *() { return m_RenderBuffer; }
+ NVRenderRenderBuffer *operator->()
+ {
+ QT3DS_ASSERT(m_RenderBuffer);
+ return m_RenderBuffer;
+ }
+ NVRenderRenderBuffer &operator*()
+ {
+ QT3DS_ASSERT(m_RenderBuffer);
+ return *m_RenderBuffer;
+ }
+ };
+}
+}
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.cpp
new file mode 100644
index 0000000..3593688
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.cpp
@@ -0,0 +1,436 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderResourceManager.h"
+#include "render/Qt3DSRenderContext.h"
+#include "render/Qt3DSRenderFrameBuffer.h"
+#include "render/Qt3DSRenderRenderBuffer.h"
+#include "render/Qt3DSRenderTexture2D.h"
+#include "render/Qt3DSRenderTexture2DArray.h"
+#include "render/Qt3DSRenderTextureCube.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSContainers.h"
+
+using namespace qt3ds::render;
+
+namespace {
+
+struct SResourceManager : public IResourceManager
+{
+ NVScopedRefCounted<NVRenderContext> m_RenderContext;
+ // Complete list of all allocated objects
+ nvvector<NVScopedRefCounted<NVRefCounted>> m_AllocatedObjects;
+
+ nvvector<NVRenderFrameBuffer *> m_FreeFrameBuffers;
+ nvvector<NVRenderRenderBuffer *> m_FreeRenderBuffers;
+ nvvector<NVRenderTexture2D *> m_FreeTextures;
+ nvvector<NVRenderTexture2DArray *> m_FreeTexArrays;
+ nvvector<NVRenderTextureCube *> m_FreeTexCubes;
+ nvvector<NVRenderImage2D *> m_FreeImages;
+
+ volatile QT3DSI32 mRefCount;
+
+ SResourceManager(NVRenderContext &ctx)
+ : m_RenderContext(ctx)
+ , m_AllocatedObjects(ctx.GetAllocator(), "SResourceManager::m_FrameBuffers")
+ , m_FreeFrameBuffers(ctx.GetAllocator(), "SResourceManager::m_FreeFrameBuffers")
+ , m_FreeRenderBuffers(ctx.GetAllocator(), "SResourceManager::m_FreeRenderBuffers")
+ , m_FreeTextures(ctx.GetAllocator(), "SResourceManager::m_FreeTextures")
+ , m_FreeTexArrays(ctx.GetAllocator(), "SResourceManager::m_FreeTexArrays")
+ , m_FreeTexCubes(ctx.GetAllocator(), "SResourceManager::m_FreeTexCubes")
+ , m_FreeImages(ctx.GetAllocator(), "SResourceManager::m_FreeImages")
+ , mRefCount(0)
+ {
+ }
+ virtual ~SResourceManager() {}
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext->GetAllocator())
+
+ NVRenderFrameBuffer *AllocateFrameBuffer() override
+ {
+ if (m_FreeFrameBuffers.empty() == true) {
+ NVRenderFrameBuffer *newBuffer = m_RenderContext->CreateFrameBuffer();
+ m_AllocatedObjects.push_back(newBuffer);
+ m_FreeFrameBuffers.push_back(newBuffer);
+ }
+ NVRenderFrameBuffer *retval = m_FreeFrameBuffers.back();
+ m_FreeFrameBuffers.pop_back();
+ return retval;
+ }
+ void Release(NVRenderFrameBuffer &inBuffer) override
+ {
+ if (inBuffer.HasAnyAttachment()) {
+ // Ensure the framebuffer has no attachments.
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Color0,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Color1,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Color2,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Color3,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Color4,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Color5,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Color6,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Color7,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Depth,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ inBuffer.Attach(NVRenderFrameBufferAttachments::Stencil,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ if (m_RenderContext->IsDepthStencilSupported())
+ inBuffer.Attach(NVRenderFrameBufferAttachments::DepthStencil,
+ qt3ds::render::NVRenderTextureOrRenderBuffer());
+ }
+#ifdef _DEBUG
+ nvvector<NVRenderFrameBuffer *>::iterator theFind =
+ eastl::find(m_FreeFrameBuffers.begin(), m_FreeFrameBuffers.end(), &inBuffer);
+ QT3DS_ASSERT(theFind == m_FreeFrameBuffers.end());
+#endif
+ m_FreeFrameBuffers.push_back(&inBuffer);
+ }
+
+ virtual NVRenderRenderBuffer *
+ AllocateRenderBuffer(QT3DSU32 inWidth, QT3DSU32 inHeight,
+ NVRenderRenderBufferFormats::Enum inBufferFormat) override
+ {
+ // Look for one of this specific size and format.
+ QT3DSU32 existingMatchIdx = m_FreeRenderBuffers.size();
+ for (QT3DSU32 idx = 0, end = existingMatchIdx; idx < end; ++idx) {
+ NVRenderRenderBuffer *theBuffer = m_FreeRenderBuffers[idx];
+ qt3ds::render::NVRenderRenderBufferDimensions theDims = theBuffer->GetDimensions();
+ NVRenderRenderBufferFormats::Enum theFormat = theBuffer->GetStorageFormat();
+ if (theDims.m_Width == inWidth && theDims.m_Height == inHeight
+ && theFormat == inBufferFormat) {
+ // Replace idx with last for efficient erasure (that reorders the vector).
+ m_FreeRenderBuffers.replace_with_last(idx);
+ return theBuffer;
+ } else if (theFormat == inBufferFormat)
+ existingMatchIdx = idx;
+ }
+ // If a specific exact match couldn't be found, just use the buffer with
+ // the same format and resize it.
+ if (existingMatchIdx < m_FreeRenderBuffers.size()) {
+ NVRenderRenderBuffer *theBuffer = m_FreeRenderBuffers[existingMatchIdx];
+ m_FreeRenderBuffers.replace_with_last(existingMatchIdx);
+ theBuffer->SetDimensions(qt3ds::render::NVRenderRenderBufferDimensions(inWidth, inHeight));
+ return theBuffer;
+ }
+
+ NVRenderRenderBuffer *theBuffer =
+ m_RenderContext->CreateRenderBuffer(inBufferFormat, inWidth, inHeight);
+ m_AllocatedObjects.push_back(theBuffer);
+ return theBuffer;
+ }
+ void Release(NVRenderRenderBuffer &inBuffer) override
+ {
+#ifdef _DEBUG
+ nvvector<NVRenderRenderBuffer *>::iterator theFind =
+ eastl::find(m_FreeRenderBuffers.begin(), m_FreeRenderBuffers.end(), &inBuffer);
+ QT3DS_ASSERT(theFind == m_FreeRenderBuffers.end());
+#endif
+ m_FreeRenderBuffers.push_back(&inBuffer);
+ }
+ NVRenderTexture2D *SetupAllocatedTexture(NVRenderTexture2D &inTexture)
+ {
+ inTexture.SetMinFilter(NVRenderTextureMinifyingOp::Linear);
+ inTexture.SetMagFilter(NVRenderTextureMagnifyingOp::Linear);
+ return &inTexture;
+ }
+ NVRenderTexture2D *AllocateTexture2D(QT3DSU32 inWidth, QT3DSU32 inHeight,
+ NVRenderTextureFormats::Enum inTextureFormat,
+ QT3DSU32 inSampleCount, bool immutable) override
+ {
+ bool inMultisample =
+ inSampleCount > 1 && m_RenderContext->AreMultisampleTexturesSupported();
+ for (QT3DSU32 idx = 0, end = m_FreeTextures.size(); idx < end; ++idx) {
+ NVRenderTexture2D *theTexture = m_FreeTextures[idx];
+ STextureDetails theDetails = theTexture->GetTextureDetails();
+ if (theDetails.m_Width == inWidth && theDetails.m_Height == inHeight
+ && inTextureFormat == theDetails.m_Format
+ && theTexture->GetSampleCount() == inSampleCount) {
+ m_FreeTextures.replace_with_last(idx);
+ return SetupAllocatedTexture(*theTexture);
+ }
+ }
+ // else resize an existing texture. This is very expensive
+ // note that MSAA textures are not resizable ( in GLES )
+ /*
+ if ( !m_FreeTextures.empty() && !inMultisample )
+ {
+ NVRenderTexture2D* theTexture = m_FreeTextures.back();
+ m_FreeTextures.pop_back();
+
+ // note we could re-use a former MSAA texture
+ // this causes a entiere destroy of the previous texture object
+ theTexture->SetTextureData( NVDataRef<QT3DSU8>(), 0, inWidth, inHeight, inTextureFormat
+ );
+
+ return SetupAllocatedTexture( *theTexture );
+ }*/
+ // else create a new texture.
+ NVRenderTexture2D *theTexture = m_RenderContext->CreateTexture2D();
+
+ if (inMultisample)
+ theTexture->SetTextureDataMultisample(inSampleCount, inWidth, inHeight,
+ inTextureFormat);
+ else if (immutable)
+ theTexture->SetTextureStorage(1, inWidth, inHeight, inTextureFormat);
+ else
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, inWidth, inHeight, inTextureFormat);
+
+ m_AllocatedObjects.push_back(theTexture);
+ return SetupAllocatedTexture(*theTexture);
+ }
+ void Release(NVRenderTexture2D &inBuffer) override
+ {
+#ifdef _DEBUG
+ nvvector<NVRenderTexture2D *>::iterator theFind =
+ eastl::find(m_FreeTextures.begin(), m_FreeTextures.end(), &inBuffer);
+ QT3DS_ASSERT(theFind == m_FreeTextures.end());
+#endif
+ m_FreeTextures.push_back(&inBuffer);
+ }
+
+ NVRenderTexture2DArray *AllocateTexture2DArray(QT3DSU32 inWidth, QT3DSU32 inHeight, QT3DSU32 inSlices,
+ NVRenderTextureFormats::Enum inTextureFormat,
+ QT3DSU32 inSampleCount) override
+ {
+ bool inMultisample =
+ inSampleCount > 1 && m_RenderContext->AreMultisampleTexturesSupported();
+ for (QT3DSU32 idx = 0, end = m_FreeTexArrays.size(); idx < end; ++idx) {
+ NVRenderTexture2DArray *theTexture = m_FreeTexArrays[idx];
+ STextureDetails theDetails = theTexture->GetTextureDetails();
+ if (theDetails.m_Width == inWidth && theDetails.m_Height == inHeight
+ && theDetails.m_Depth == inSlices && inTextureFormat == theDetails.m_Format
+ && theTexture->GetSampleCount() == inSampleCount) {
+ m_FreeTexArrays.replace_with_last(idx);
+ theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear);
+ theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear);
+ return theTexture;
+ }
+ }
+
+ // else resize an existing texture. This should be fairly quick at the driver level.
+ // note that MSAA textures are not resizable ( in GLES )
+ if (!m_FreeTexArrays.empty() && !inMultisample) {
+ NVRenderTexture2DArray *theTexture = m_FreeTexArrays.back();
+ m_FreeTexArrays.pop_back();
+
+ // note we could re-use a former MSAA texture
+ // this causes a entiere destroy of the previous texture object
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, inWidth, inHeight, inSlices,
+ inTextureFormat);
+ theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear);
+ theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear);
+ return theTexture;
+ }
+
+ // else create a new texture.
+ NVRenderTexture2DArray *theTexture = NULL;
+
+ if (!inMultisample) {
+ theTexture = m_RenderContext->CreateTexture2DArray();
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, inWidth, inHeight, inSlices,
+ inTextureFormat);
+ } else {
+ // Not supported yet
+ return NULL;
+ }
+
+ m_AllocatedObjects.push_back(theTexture);
+ theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear);
+ theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear);
+ return theTexture;
+ }
+
+ void Release(NVRenderTexture2DArray &inBuffer) override
+ {
+#ifdef _DEBUG
+ nvvector<NVRenderTexture2DArray *>::iterator theFind =
+ eastl::find(m_FreeTexArrays.begin(), m_FreeTexArrays.end(), &inBuffer);
+ QT3DS_ASSERT(theFind == m_FreeTexArrays.end());
+#endif
+ m_FreeTexArrays.push_back(&inBuffer);
+ }
+
+ NVRenderTextureCube *AllocateTextureCube(QT3DSU32 inWidth, QT3DSU32 inHeight,
+ NVRenderTextureFormats::Enum inTextureFormat,
+ QT3DSU32 inSampleCount) override
+ {
+ bool inMultisample =
+ inSampleCount > 1 && m_RenderContext->AreMultisampleTexturesSupported();
+ for (QT3DSU32 idx = 0, end = m_FreeTexCubes.size(); idx < end; ++idx) {
+ NVRenderTextureCube *theTexture = m_FreeTexCubes[idx];
+ STextureDetails theDetails = theTexture->GetTextureDetails();
+ if (theDetails.m_Width == inWidth && theDetails.m_Height == inHeight
+ && inTextureFormat == theDetails.m_Format
+ && theTexture->GetSampleCount() == inSampleCount) {
+ m_FreeTexCubes.replace_with_last(idx);
+
+ theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear);
+ theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear);
+ return theTexture;
+ }
+ }
+
+ // else resize an existing texture. This should be fairly quick at the driver level.
+ // note that MSAA textures are not resizable ( in GLES )
+ if (!m_FreeTexCubes.empty() && !inMultisample) {
+ NVRenderTextureCube *theTexture = m_FreeTexCubes.back();
+ m_FreeTexCubes.pop_back();
+
+ // note we could re-use a former MSAA texture
+ // this causes a entire destroy of the previous texture object
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosX,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegX,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosY,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegY,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosZ,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegZ,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear);
+ theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear);
+ return theTexture;
+ }
+
+ // else create a new texture.
+ NVRenderTextureCube *theTexture = NULL;
+
+ if (!inMultisample) {
+ theTexture = m_RenderContext->CreateTextureCube();
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosX,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegX,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosY,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegY,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubePosZ,
+ inWidth, inHeight, inTextureFormat);
+ theTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, NVRenderTextureCubeFaces::CubeNegZ,
+ inWidth, inHeight, inTextureFormat);
+ } else {
+ // Not supported yet
+ return NULL;
+ }
+
+ m_AllocatedObjects.push_back(theTexture);
+ theTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear);
+ theTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear);
+ return theTexture;
+ }
+
+ void Release(NVRenderTextureCube &inBuffer) override
+ {
+#ifdef _DEBUG
+ nvvector<NVRenderTextureCube *>::iterator theFind =
+ eastl::find(m_FreeTexCubes.begin(), m_FreeTexCubes.end(), &inBuffer);
+ QT3DS_ASSERT(theFind == m_FreeTexCubes.end());
+#endif
+ m_FreeTexCubes.push_back(&inBuffer);
+ }
+
+ NVRenderImage2D *AllocateImage2D(NVRenderTexture2D *inTexture,
+ NVRenderImageAccessType::Enum inAccess) override
+ {
+ if (m_FreeImages.empty() == true) {
+ NVRenderImage2D *newImage = m_RenderContext->CreateImage2D(inTexture, inAccess);
+ if (newImage) {
+ m_AllocatedObjects.push_back(newImage);
+ m_FreeImages.push_back(newImage);
+ }
+ }
+
+ NVRenderImage2D *retval = m_FreeImages.back();
+ m_FreeImages.pop_back();
+
+ return retval;
+ }
+
+ void Release(NVRenderImage2D &inBuffer) override
+ {
+#ifdef _DEBUG
+ nvvector<NVRenderImage2D *>::iterator theFind =
+ eastl::find(m_FreeImages.begin(), m_FreeImages.end(), &inBuffer);
+ QT3DS_ASSERT(theFind == m_FreeImages.end());
+#endif
+ m_FreeImages.push_back(&inBuffer);
+ }
+
+ NVRenderContext &GetRenderContext() override { return *m_RenderContext; }
+
+ void RemoveObjectAllocation(NVRefCounted *obj) {
+ for (QT3DSU32 idx = 0, end = m_AllocatedObjects.size(); idx < end; ++idx) {
+ if (obj == m_AllocatedObjects[idx]) {
+ m_AllocatedObjects.replace_with_last(idx);
+ break;
+ }
+ }
+ }
+
+ void DestroyFreeSizedResources()
+ {
+ for (int idx = m_FreeRenderBuffers.size() - 1; idx >= 0; --idx) {
+ NVRenderRenderBuffer *obj = m_FreeRenderBuffers[idx];
+ m_FreeRenderBuffers.replace_with_last(idx);
+ RemoveObjectAllocation(obj);
+ }
+ for (int idx = m_FreeTextures.size() - 1; idx >= 0; --idx) {
+ NVRenderTexture2D *obj = m_FreeTextures[idx];
+ m_FreeTextures.replace_with_last(idx);
+ RemoveObjectAllocation(obj);
+ }
+ for (int idx = m_FreeTexArrays.size() - 1; idx >= 0; --idx) {
+ NVRenderTexture2DArray *obj = m_FreeTexArrays[idx];
+ m_FreeTexArrays.replace_with_last(idx);
+ RemoveObjectAllocation(obj);
+ }
+ for (int idx = m_FreeTexCubes.size() - 1; idx >= 0; --idx) {
+ NVRenderTextureCube *obj = m_FreeTexCubes[idx];
+ m_FreeTexCubes.replace_with_last(idx);
+ RemoveObjectAllocation(obj);
+ }
+ }
+};
+}
+
+IResourceManager &IResourceManager::CreateResourceManager(NVRenderContext &inContext)
+{
+ return *QT3DS_NEW(inContext.GetAllocator(), SResourceManager)(inContext);
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.h b/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.h
new file mode 100644
index 0000000..675d644
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceManager.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_RENDER_RESOURCE_MANAGER_H
+#define QT3DS_RENDER_RESOURCE_MANAGER_H
+#include "Qt3DSRender.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+
+namespace qt3ds {
+namespace render {
+ /**
+ * Implements simple pooling of render resources
+ */
+ class IResourceManager : public NVRefCounted
+ {
+ protected:
+ virtual ~IResourceManager() {}
+
+ public:
+ virtual NVRenderFrameBuffer *AllocateFrameBuffer() = 0;
+ virtual void Release(NVRenderFrameBuffer &inBuffer) = 0;
+ virtual NVRenderRenderBuffer *
+ AllocateRenderBuffer(QT3DSU32 inWidth, QT3DSU32 inHeight,
+ NVRenderRenderBufferFormats::Enum inBufferFormat) = 0;
+ virtual void Release(NVRenderRenderBuffer &inBuffer) = 0;
+ virtual NVRenderTexture2D *AllocateTexture2D(QT3DSU32 inWidth, QT3DSU32 inHeight,
+ NVRenderTextureFormats::Enum inTextureFormat,
+ QT3DSU32 inSampleCount = 1,
+ bool immutable = false) = 0;
+ virtual void Release(NVRenderTexture2D &inBuffer) = 0;
+ virtual NVRenderTexture2DArray *
+ AllocateTexture2DArray(QT3DSU32 inWidth, QT3DSU32 inHeight, QT3DSU32 inSlices,
+ NVRenderTextureFormats::Enum inTextureFormat,
+ QT3DSU32 inSampleCount = 1) = 0;
+ virtual void Release(NVRenderTexture2DArray &inBuffer) = 0;
+ virtual NVRenderTextureCube *
+ AllocateTextureCube(QT3DSU32 inWidth, QT3DSU32 inHeight,
+ NVRenderTextureFormats::Enum inTextureFormat,
+ QT3DSU32 inSampleCount = 1) = 0;
+ virtual void Release(NVRenderTextureCube &inBuffer) = 0;
+ virtual NVRenderImage2D *AllocateImage2D(NVRenderTexture2D *inTexture,
+ NVRenderImageAccessType::Enum inAccess) = 0;
+ virtual void Release(NVRenderImage2D &inBuffer) = 0;
+
+ virtual NVRenderContext &GetRenderContext() = 0;
+ virtual void DestroyFreeSizedResources() = 0;
+
+ static IResourceManager &CreateResourceManager(NVRenderContext &inContext);
+ };
+}
+}
+
+#endif
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.cpp
new file mode 100644
index 0000000..e877cbc
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderResourceTexture2D.h"
+
+using namespace qt3ds::render;
+
+CResourceTexture2D::CResourceTexture2D(IResourceManager &mgr, NVRenderTexture2D *inTexture)
+ : m_ResourceManager(mgr)
+ , m_Texture(inTexture)
+{
+ if (inTexture)
+ m_TextureDetails = inTexture->GetTextureDetails();
+}
+
+CResourceTexture2D::CResourceTexture2D(IResourceManager &mgr, QT3DSU32 width, QT3DSU32 height,
+ NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples)
+ : m_ResourceManager(mgr)
+ , m_Texture(NULL)
+{
+ EnsureTexture(width, height, inFormat, inSamples);
+}
+
+CResourceTexture2D::~CResourceTexture2D()
+{
+ ReleaseTexture();
+}
+
+// Returns true if the texture was allocated, false if nothing changed (no allocation).
+bool CResourceTexture2D::TextureMatches(QT3DSU32 width, QT3DSU32 height,
+ NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples)
+{
+ return m_Texture && m_TextureDetails.m_Width == width && m_TextureDetails.m_Height == height
+ && m_TextureDetails.m_Format == inFormat && m_TextureDetails.m_SampleCount == inSamples;
+}
+
+bool CResourceTexture2D::EnsureTexture(QT3DSU32 width, QT3DSU32 height,
+ NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples)
+{
+ if (TextureMatches(width, height, inFormat, inSamples))
+ return false;
+
+ if (m_Texture && inSamples > 1) {
+ // we cannot resize MSAA textures though release first
+ ReleaseTexture();
+ }
+
+ if (!m_Texture)
+ m_Texture = m_ResourceManager.AllocateTexture2D(width, height, inFormat, inSamples);
+ else {
+ // multisampled textures are immuteable
+ QT3DS_ASSERT(inSamples == 1);
+ m_Texture->SetTextureData(NVDataRef<QT3DSU8>(), 0, width, height, inFormat);
+ }
+
+ m_TextureDetails = m_Texture->GetTextureDetails();
+ return true;
+}
+
+void CResourceTexture2D::ReleaseTexture()
+{
+ if (m_Texture) {
+ m_ResourceManager.Release(*m_Texture);
+ ForgetTexture();
+ }
+}
+
+void CResourceTexture2D::ForgetTexture()
+{
+ m_Texture = NULL;
+}
+
+void CResourceTexture2D::StealTexture(CResourceTexture2D &inOther)
+{
+ ReleaseTexture();
+ m_Texture = inOther.m_Texture;
+ m_TextureDetails = inOther.m_TextureDetails;
+ inOther.m_Texture = NULL;
+}
+
+CResourceTexture2DArray::CResourceTexture2DArray(IResourceManager &mgr)
+ : m_ResourceManager(mgr)
+ , m_Texture(NULL)
+{
+}
+
+CResourceTexture2DArray::CResourceTexture2DArray(IResourceManager &mgr, QT3DSU32 width, QT3DSU32 height,
+ QT3DSU32 slices,
+ NVRenderTextureFormats::Enum inFormat,
+ QT3DSU32 inSamples)
+ : m_ResourceManager(mgr)
+ , m_Texture(NULL)
+{
+ EnsureTexture(width, height, slices, inFormat, inSamples);
+}
+
+CResourceTexture2DArray::~CResourceTexture2DArray()
+{
+ ReleaseTexture();
+}
+
+bool CResourceTexture2DArray::TextureMatches(QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices,
+ NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples)
+{
+ return m_Texture && m_TextureDetails.m_Depth == slices && m_TextureDetails.m_Width == width
+ && m_TextureDetails.m_Height == height && m_TextureDetails.m_Format == inFormat
+ && m_TextureDetails.m_SampleCount == inSamples;
+}
+
+bool CResourceTexture2DArray::EnsureTexture(QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices,
+ NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples)
+{
+ if (TextureMatches(width, height, slices, inFormat, inSamples))
+ return false;
+
+ if (m_Texture && inSamples > 1) {
+ // we cannot resize MSAA textures though release first
+ ReleaseTexture();
+ }
+
+ if (!m_Texture)
+ m_Texture =
+ m_ResourceManager.AllocateTexture2DArray(width, height, slices, inFormat, inSamples);
+ else {
+ // multisampled textures are immuteable
+ QT3DS_ASSERT(inSamples == 1);
+ m_Texture->SetTextureData(NVDataRef<QT3DSU8>(), 0, width, height, slices, inFormat);
+ }
+
+ m_TextureDetails = m_Texture->GetTextureDetails();
+ return true;
+}
+
+void CResourceTexture2DArray::ReleaseTexture()
+{
+ if (m_Texture) {
+ m_ResourceManager.Release(*m_Texture);
+ m_Texture = NULL;
+ }
+}
+
+void CResourceTexture2DArray::StealTexture(CResourceTexture2DArray &inOther)
+{
+ ReleaseTexture();
+ m_Texture = inOther.m_Texture;
+ m_TextureDetails = inOther.m_TextureDetails;
+ inOther.m_Texture = NULL;
+}
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.h b/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.h
new file mode 100644
index 0000000..eb54713
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderResourceTexture2D.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_RENDER_RESOURCE_TEXTURE_2D_H
+#define QT3DS_RENDER_RESOURCE_TEXTURE_2D_H
+#include "Qt3DSRender.h"
+#include "render/Qt3DSRenderContext.h"
+#include "render/Qt3DSRenderTexture2D.h"
+#include "render/Qt3DSRenderTexture2DArray.h"
+#include "Qt3DSRenderResourceManager.h"
+
+namespace qt3ds {
+namespace render {
+ class CResourceTexture2D
+ {
+ protected:
+ IResourceManager &m_ResourceManager;
+ NVRenderTexture2D *m_Texture;
+ STextureDetails m_TextureDetails;
+
+ public:
+ CResourceTexture2D(IResourceManager &mgr, NVRenderTexture2D *inTexture = NULL);
+ // create and allocate the texture right away.
+ CResourceTexture2D(IResourceManager &mgr, QT3DSU32 width, QT3DSU32 height,
+ NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples = 1);
+ ~CResourceTexture2D();
+ // Returns true if the texture matches the specs, false if the texture needs to be
+ // reallocated
+ bool TextureMatches(QT3DSU32 width, QT3DSU32 height, NVRenderTextureFormats::Enum inFormat,
+ QT3DSU32 inSamples = 1);
+
+ // Returns true if the texture was allocated, false if nothing changed (no allocation).
+ // Note this is the exact opposite of TextureMatches.
+ bool EnsureTexture(QT3DSU32 width, QT3DSU32 height, NVRenderTextureFormats::Enum inFormat,
+ QT3DSU32 inSamples = 1);
+
+ // Force release the texture.
+ void ReleaseTexture();
+ NVRenderTexture2D &operator*()
+ {
+ QT3DS_ASSERT(m_Texture);
+ return *m_Texture;
+ }
+ NVRenderTexture2D *operator->()
+ {
+ QT3DS_ASSERT(m_Texture);
+ return m_Texture;
+ }
+ operator NVRenderTexture2D *() { return m_Texture; }
+ NVRenderTexture2D *GetTexture() { return m_Texture; }
+ void ForgetTexture();
+ // Enforces single ownership rules.
+ void StealTexture(CResourceTexture2D &inOther);
+ };
+
+ class CResourceTexture2DArray
+ {
+ protected:
+ IResourceManager &m_ResourceManager;
+ qt3ds::render::NVRenderTexture2DArray *m_Texture;
+ STextureDetails m_TextureDetails;
+
+ public:
+ CResourceTexture2DArray(IResourceManager &mgr);
+ // create and allocate the texture right away.
+ CResourceTexture2DArray(IResourceManager &mgr, QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices,
+ NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples = 1);
+ ~CResourceTexture2DArray();
+ // Returns true if the texture matches the specs, false if the texture needs to be
+ // reallocated
+ bool TextureMatches(QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices,
+ NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples = 1);
+
+ // Returns true if the texture was allocated, false if nothing changed (no allocation).
+ // Note this is the exact opposite of TextureMatches.
+ bool EnsureTexture(QT3DSU32 width, QT3DSU32 height, QT3DSU32 slices,
+ NVRenderTextureFormats::Enum inFormat, QT3DSU32 inSamples = 1);
+
+ // Force release the texture.
+ void ReleaseTexture();
+ qt3ds::render::NVRenderTexture2DArray &operator*()
+ {
+ QT3DS_ASSERT(m_Texture);
+ return *m_Texture;
+ }
+ qt3ds::render::NVRenderTexture2DArray *operator->()
+ {
+ QT3DS_ASSERT(m_Texture);
+ return m_Texture;
+ }
+ operator qt3ds::render::NVRenderTexture2DArray *() { return m_Texture; }
+ qt3ds::render::NVRenderTexture2DArray *GetTexture() { return m_Texture; }
+ // Enforces single ownership rules.
+ void StealTexture(CResourceTexture2DArray &inOther);
+ };
+}
+}
+
+#endif