mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-13 15:44:36 +00:00
Merge remote-tracking branch 'upstream/development' into development
This commit is contained in:
commit
4fa4233ae4
19 changed files with 694 additions and 898 deletions
|
|
@ -190,12 +190,6 @@ SoundAsset::SoundAsset()
|
||||||
SoundAsset::~SoundAsset()
|
SoundAsset::~SoundAsset()
|
||||||
{
|
{
|
||||||
|
|
||||||
for (U32 i = 0; i < SFXPlayList::NUM_SLOTS; i++)
|
|
||||||
{
|
|
||||||
if(mSFXProfile[i].isProperlyAdded() && !mSFXProfile[i].isDeleted())
|
|
||||||
mSFXProfile[i].unregisterObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mPlaylist.isProperlyAdded() && !mPlaylist.isDeleted())
|
if (mPlaylist.isProperlyAdded() && !mPlaylist.isDeleted())
|
||||||
mPlaylist.unregisterObject();
|
mPlaylist.unregisterObject();
|
||||||
}
|
}
|
||||||
|
|
@ -404,22 +398,17 @@ U32 SoundAsset::load()
|
||||||
{
|
{
|
||||||
Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile[i]);
|
Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile[i]);
|
||||||
mLoadedState = BadFileReference;
|
mLoadedState = BadFileReference;
|
||||||
mSFXProfile[i].setDescription(NULL);
|
|
||||||
mSFXProfile[i].setSoundFileName(StringTable->insert(StringTable->EmptyString()));
|
|
||||||
mSFXProfile[i].setPreload(false);
|
|
||||||
return mLoadedState;
|
return mLoadedState;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SFXProfile* trackProfile = new SFXProfile();
|
mSFXProfile[i] = new SFXProfile;
|
||||||
trackProfile->setDescription(&mProfileDesc);
|
mSFXProfile[i]->setDescription(&mProfileDesc);
|
||||||
trackProfile->setSoundFileName(mSoundPath[i]);
|
mSFXProfile[i]->setSoundFileName(mSoundPath[i]);
|
||||||
trackProfile->setPreload(mPreload);
|
mSFXProfile[i]->setPreload(mPreload);
|
||||||
|
mSFXProfile[i]->registerObject(String::ToString("%s_profile_track%d", getAssetName()).c_str());
|
||||||
|
|
||||||
mSFXProfile[i] = *trackProfile;
|
mPlaylist.mSlots.mTrack[i] = mSFXProfile[i];
|
||||||
mSFXProfile[i].registerObject(String::ToString("%s_profile_track%d", getAssetName()).c_str());
|
|
||||||
|
|
||||||
mPlaylist.mSlots.mTrack[i] = trackProfile;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -436,18 +425,15 @@ U32 SoundAsset::load()
|
||||||
{
|
{
|
||||||
Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile[0]);
|
Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile[0]);
|
||||||
mLoadedState = BadFileReference;
|
mLoadedState = BadFileReference;
|
||||||
mSFXProfile[0].setDescription(NULL);
|
|
||||||
mSFXProfile[0].setSoundFileName(StringTable->insert(StringTable->EmptyString()));
|
|
||||||
mSFXProfile[0].setPreload(false);
|
|
||||||
return mLoadedState;
|
return mLoadedState;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mSFXProfile[0].setDescription(&mProfileDesc);
|
mSFXProfile[0] = new SFXProfile;
|
||||||
mSFXProfile[0].setSoundFileName(mSoundPath[0]);
|
mSFXProfile[0]->setDescription(&mProfileDesc);
|
||||||
mSFXProfile[0].setPreload(mPreload);
|
mSFXProfile[0]->setSoundFileName(mSoundPath[0]);
|
||||||
|
mSFXProfile[0]->setPreload(mPreload);
|
||||||
mSFXProfile[0].registerObject(String::ToString("%s_profile", getAssetName()).c_str());
|
mSFXProfile[0]->registerObject(String::ToString("%s_profile", getAssetName()).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ class SoundAsset : public AssetBase
|
||||||
protected:
|
protected:
|
||||||
StringTableEntry mSoundFile[SFXPlayList::SFXPlaylistSettings::NUM_SLOTS];
|
StringTableEntry mSoundFile[SFXPlayList::SFXPlaylistSettings::NUM_SLOTS];
|
||||||
StringTableEntry mSoundPath[SFXPlayList::SFXPlaylistSettings::NUM_SLOTS];
|
StringTableEntry mSoundPath[SFXPlayList::SFXPlaylistSettings::NUM_SLOTS];
|
||||||
SFXProfile mSFXProfile[SFXPlayList::SFXPlaylistSettings::NUM_SLOTS];
|
SimObjectPtr<SFXProfile> mSFXProfile[SFXPlayList::SFXPlaylistSettings::NUM_SLOTS];
|
||||||
|
|
||||||
SFXDescription mProfileDesc;
|
SFXDescription mProfileDesc;
|
||||||
SFXPlayList mPlaylist;
|
SFXPlayList mPlaylist;
|
||||||
|
|
@ -150,7 +150,7 @@ public:
|
||||||
void copyTo(SimObject* object) override;
|
void copyTo(SimObject* object) override;
|
||||||
|
|
||||||
//SFXResource* getSound() { return mSoundResource; }
|
//SFXResource* getSound() { return mSoundResource; }
|
||||||
Resource<SFXResource> getSoundResource(const U32 slotId = 0) { load(); return mSFXProfile[slotId].getResource(); }
|
Resource<SFXResource> getSoundResource(const U32 slotId = 0) { load(); return mSFXProfile[slotId]->getResource(); }
|
||||||
|
|
||||||
/// Declare Console Object.
|
/// Declare Console Object.
|
||||||
DECLARE_CONOBJECT(SoundAsset);
|
DECLARE_CONOBJECT(SoundAsset);
|
||||||
|
|
@ -158,9 +158,9 @@ public:
|
||||||
static bool _setSoundFile(void* object, const char* index, const char* data);
|
static bool _setSoundFile(void* object, const char* index, const char* data);
|
||||||
U32 load() override;
|
U32 load() override;
|
||||||
inline StringTableEntry getSoundPath(const U32 slotId = 0) const { return mSoundPath[slotId]; };
|
inline StringTableEntry getSoundPath(const U32 slotId = 0) const { return mSoundPath[slotId]; };
|
||||||
SFXProfile* getSfxProfile(const U32 slotId = 0) { return &mSFXProfile[slotId]; }
|
SFXProfile* getSfxProfile(const U32 slotId = 0) { return mSFXProfile[slotId]; }
|
||||||
SFXPlayList* getSfxPlaylist() { return &mPlaylist; }
|
SFXPlayList* getSfxPlaylist() { return &mPlaylist; }
|
||||||
SFXTrack* getSFXTrack() { load(); return mIsPlaylist ? dynamic_cast<SFXTrack*>(&mPlaylist) : dynamic_cast<SFXTrack*>(&mSFXProfile[0]); }
|
SFXTrack* getSFXTrack() { load(); return mIsPlaylist ? dynamic_cast<SFXTrack*>(&mPlaylist) : dynamic_cast<SFXTrack*>(mSFXProfile[0].getPointer()); }
|
||||||
SFXDescription* getSfxDescription() { return &mProfileDesc; }
|
SFXDescription* getSfxDescription() { return &mProfileDesc; }
|
||||||
bool isPlaylist(){ return mIsPlaylist; }
|
bool isPlaylist(){ return mIsPlaylist; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -471,6 +471,7 @@ bool PlayerData::preload(bool server, String &errorStr)
|
||||||
if (!server) {
|
if (!server) {
|
||||||
for (U32 i = 0; i < MaxSounds; ++i)
|
for (U32 i = 0; i < MaxSounds; ++i)
|
||||||
{
|
{
|
||||||
|
_setPlayerSound(getPlayerSound(i), i);
|
||||||
if (!isPlayerSoundValid(i))
|
if (!isPlayerSoundValid(i))
|
||||||
{
|
{
|
||||||
//return false; -TODO: trigger asset download
|
//return false; -TODO: trigger asset download
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,7 @@ bool RigidShapeData::preload(bool server, String &errorStr)
|
||||||
if (!server) {
|
if (!server) {
|
||||||
for (S32 i = 0; i < Body::MaxSounds; i++)
|
for (S32 i = 0; i < Body::MaxSounds; i++)
|
||||||
{
|
{
|
||||||
|
_setBodySounds(getBodySounds(i), i);
|
||||||
if (!isBodySoundsValid(i))
|
if (!isBodySoundsValid(i))
|
||||||
{
|
{
|
||||||
//return false; -TODO: trigger asset download
|
//return false; -TODO: trigger asset download
|
||||||
|
|
@ -356,6 +357,7 @@ bool RigidShapeData::preload(bool server, String &errorStr)
|
||||||
|
|
||||||
for (S32 j = 0; j < Sounds::MaxSounds; j++)
|
for (S32 j = 0; j < Sounds::MaxSounds; j++)
|
||||||
{
|
{
|
||||||
|
_setWaterSounds(getWaterSounds(j), j);
|
||||||
if (!isWaterSoundsValid(j))
|
if (!isWaterSoundsValid(j))
|
||||||
{
|
{
|
||||||
//return false; -TODO: trigger asset download
|
//return false; -TODO: trigger asset download
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@ bool FlyingVehicleData::preload(bool server, String &errorStr)
|
||||||
if (!server) {
|
if (!server) {
|
||||||
for (S32 i = 0; i < MaxSounds; i++)
|
for (S32 i = 0; i < MaxSounds; i++)
|
||||||
{
|
{
|
||||||
|
_setFlyingSounds(getFlyingSounds(i), i);
|
||||||
if (!isFlyingSoundsValid(i))
|
if (!isFlyingSoundsValid(i))
|
||||||
{
|
{
|
||||||
//return false; -TODO: trigger asset download
|
//return false; -TODO: trigger asset download
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,7 @@ bool HoverVehicleData::preload(bool server, String &errorStr)
|
||||||
|
|
||||||
for (S32 i = 0; i < MaxSounds; i++)
|
for (S32 i = 0; i < MaxSounds; i++)
|
||||||
{
|
{
|
||||||
|
_setHoverSounds(getHoverSounds(i), i);
|
||||||
if (!isHoverSoundsValid(i))
|
if (!isHoverSoundsValid(i))
|
||||||
{
|
{
|
||||||
//return false; -TODO: trigger asset download
|
//return false; -TODO: trigger asset download
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,7 @@ bool WheeledVehicleData::preload(bool server, String &errorStr)
|
||||||
if (!server) {
|
if (!server) {
|
||||||
for (S32 i = 0; i < MaxSounds; i++)
|
for (S32 i = 0; i < MaxSounds; i++)
|
||||||
{
|
{
|
||||||
|
_setWheeledVehicleSounds(getWheeledVehicleSounds(i), i);
|
||||||
if (!isWheeledVehicleSoundsValid(i))
|
if (!isWheeledVehicleSoundsValid(i))
|
||||||
{
|
{
|
||||||
//return false; -TODO: trigger asset download
|
//return false; -TODO: trigger asset download
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,10 @@
|
||||||
#ifndef _TAML_CALLBACKS_H_
|
#ifndef _TAML_CALLBACKS_H_
|
||||||
#include "persistence/taml/tamlCallbacks.h"
|
#include "persistence/taml/tamlCallbacks.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _OBJECTTYPES_H_
|
||||||
#include "T3D/objectTypes.h"
|
#include "T3D/objectTypes.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
class Stream;
|
class Stream;
|
||||||
class LightManager;
|
class LightManager;
|
||||||
|
|
@ -606,6 +609,7 @@ class SimObject: public ConsoleObject, public TamlCallbacks
|
||||||
|
|
||||||
/// @name Events
|
/// @name Events
|
||||||
/// @{
|
/// @{
|
||||||
|
//virtual void onPrepare();
|
||||||
|
|
||||||
/// Called when the object is added to the sim.
|
/// Called when the object is added to the sim.
|
||||||
virtual bool onAdd();
|
virtual bool onAdd();
|
||||||
|
|
@ -1049,51 +1053,42 @@ class SimObjectPtr : public WeakRefPtr< T >
|
||||||
|
|
||||||
typedef WeakRefPtr< T > Parent;
|
typedef WeakRefPtr< T > Parent;
|
||||||
|
|
||||||
SimObjectPtr() {}
|
SimObjectPtr() = default;
|
||||||
SimObjectPtr(T *ptr) { this->mReference = NULL; set(ptr); }
|
SimObjectPtr(T* ptr) { set(ptr); }
|
||||||
SimObjectPtr( const SimObjectPtr& ref ) { this->mReference = NULL; set(ref.mReference); }
|
SimObjectPtr(const SimObjectPtr&) = default;
|
||||||
|
SimObjectPtr& operator=(const SimObjectPtr&) = default;
|
||||||
T* getObject() const { return Parent::getPointer(); }
|
SimObjectPtr& operator=(T* ptr)
|
||||||
|
|
||||||
~SimObjectPtr() { set((WeakRefBase::WeakReference*)NULL); }
|
|
||||||
|
|
||||||
SimObjectPtr<T>& operator=(const SimObjectPtr ref)
|
|
||||||
{
|
|
||||||
set(ref.mReference);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
SimObjectPtr<T>& operator=(T *ptr)
|
|
||||||
{
|
{
|
||||||
set(ptr);
|
set(ptr);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T* getObject() const { return Parent::getPointer(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void set(WeakRefBase::WeakReference * ref)
|
|
||||||
{
|
|
||||||
if( ref == this->mReference )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( this->mReference )
|
|
||||||
{
|
|
||||||
// Auto-delete
|
|
||||||
T* obj = this->getPointer();
|
|
||||||
if ( this->mReference->getRefCount() == 2 && obj && obj->isAutoDeleted() )
|
|
||||||
obj->deleteObject();
|
|
||||||
|
|
||||||
this->mReference->decRefCount();
|
|
||||||
}
|
|
||||||
this->mReference = NULL;
|
|
||||||
if( ref )
|
|
||||||
{
|
|
||||||
this->mReference = ref;
|
|
||||||
this->mReference->incRefCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(T* obj)
|
void set(T* obj)
|
||||||
{
|
{
|
||||||
set(obj ? obj->getWeakReference() : (WeakRefBase::WeakReference *)NULL);
|
// Nothing to do if same object
|
||||||
|
if (obj && this->mWeak.lock() == obj->getWeakControl().lock())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Before overwriting, check old object for auto-delete
|
||||||
|
if (auto old_ctrl = this->mWeak.lock())
|
||||||
|
{
|
||||||
|
T* old_obj = getObject();
|
||||||
|
if (this->mWeak.use_count() == 1 && old_obj && old_obj->isAutoDeleted())
|
||||||
|
{
|
||||||
|
old_obj->destroySelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign new weak reference
|
||||||
|
this->mWeak.reset();
|
||||||
|
if (obj)
|
||||||
|
{
|
||||||
|
auto obj_ctrl = obj->getWeakControl().lock();
|
||||||
|
this->mWeak = obj_ctrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
18
Engine/source/core/util/refBase.cpp
Normal file
18
Engine/source/core/util/refBase.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include "refBase.h"
|
||||||
|
|
||||||
|
WeakRefBase::~WeakRefBase()
|
||||||
|
{
|
||||||
|
if (mControl)
|
||||||
|
mControl->object = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WeakControlBlock::WeakControlBlock(WeakRefBase* obj)
|
||||||
|
: object(obj)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WeakControlBlock::~WeakControlBlock()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -30,52 +30,52 @@
|
||||||
# include "platform/typetraits.h"
|
# include "platform/typetraits.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
class WeakRefBase;
|
||||||
|
|
||||||
|
struct WeakControlBlock
|
||||||
|
{
|
||||||
|
explicit WeakControlBlock(WeakRefBase* obj);
|
||||||
|
~WeakControlBlock();
|
||||||
|
|
||||||
|
WeakRefBase* object;
|
||||||
|
};
|
||||||
|
|
||||||
/// Base class for objects which can be weakly referenced
|
/// Base class for objects which can be weakly referenced
|
||||||
/// (i.e., reference goes away when object is destroyed).
|
/// (i.e., reference goes away when object is destroyed).
|
||||||
class WeakRefBase
|
class WeakRefBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
WeakRefBase()
|
||||||
/// Weak reference to WeakRefBase.
|
: mControl(std::make_shared<WeakControlBlock>(this))
|
||||||
class WeakReference
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr WeakRefBase* get() const { return mObject; }
|
|
||||||
[[nodiscard]] constexpr U32 getRefCount() const { return mRefCount; }
|
|
||||||
|
|
||||||
constexpr void incRefCount() { mRefCount++; }
|
|
||||||
constexpr void decRefCount() {
|
|
||||||
AssertFatal( mRefCount > 0, "WeakReference - decrementing count of zero!" );
|
|
||||||
if (--mRefCount==0)
|
|
||||||
delete this;
|
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
|
|
||||||
friend class WeakRefBase;
|
virtual ~WeakRefBase();
|
||||||
constexpr explicit WeakReference(WeakRefBase *object) :mObject(object), mRefCount(0) {}
|
|
||||||
|
|
||||||
~WeakReference() { AssertFatal(mObject==NULL, "Deleting weak reference which still points at an object."); }
|
// Copy constructor
|
||||||
|
WeakRefBase(const WeakRefBase&) = delete;
|
||||||
|
WeakRefBase& operator=(const WeakRefBase&) = delete;
|
||||||
|
WeakRefBase(WeakRefBase&&) = delete;
|
||||||
|
WeakRefBase& operator=(WeakRefBase&&) = delete;
|
||||||
|
|
||||||
// Object we reference
|
std::weak_ptr<WeakControlBlock> getWeakControl()
|
||||||
WeakRefBase *mObject;
|
{
|
||||||
|
return mControl;
|
||||||
|
}
|
||||||
|
|
||||||
// reference count for this structure (not WeakObjectRef itself)
|
std::shared_ptr<WeakControlBlock> getSharedControl()
|
||||||
U32 mRefCount;
|
{
|
||||||
};
|
return mControl;
|
||||||
|
}
|
||||||
public:
|
|
||||||
constexpr WeakRefBase() : mReference(NULL) {}
|
|
||||||
virtual ~WeakRefBase() { clearWeakReferences(); }
|
|
||||||
|
|
||||||
WeakReference* getWeakReference();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void clearWeakReferences();
|
|
||||||
|
|
||||||
|
std::shared_ptr<WeakControlBlock> mControl;
|
||||||
private:
|
private:
|
||||||
WeakReference * mReference;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template< typename T > class SimObjectPtr;
|
template< typename T > class SimObjectPtr;
|
||||||
|
|
@ -88,100 +88,48 @@ template< typename T > class SimObjectPtr;
|
||||||
template <class T> class WeakRefPtr
|
template <class T> class WeakRefPtr
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr WeakRefPtr() : mReference(NULL) {}
|
WeakRefPtr() = default;
|
||||||
WeakRefPtr(T *ptr) : mReference(NULL) { set(ptr); }
|
WeakRefPtr(T* obj) { set(obj); }
|
||||||
WeakRefPtr(const WeakRefPtr<T> & ref) { mReference = NULL; set(ref.mReference); }
|
|
||||||
|
|
||||||
~WeakRefPtr() { set(static_cast<WeakRefBase::WeakReference *>(NULL)); }
|
WeakRefPtr(const WeakRefPtr&) = default;
|
||||||
|
WeakRefPtr& operator=(const WeakRefPtr&) = default;
|
||||||
|
|
||||||
WeakRefPtr<T>& operator=(const WeakRefPtr<T>& ref)
|
WeakRefPtr& operator=(T* obj)
|
||||||
{
|
{
|
||||||
if (this == &ref) { return *this; } // handle self assignment ( x = x; )
|
set(obj);
|
||||||
set(ref.mReference);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
WeakRefPtr<T>& operator=(T *ptr)
|
|
||||||
{
|
|
||||||
set(ptr);
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the pointer is not set.
|
bool isValid() const { return getPointer() != NULL; }
|
||||||
[[nodiscard]] constexpr bool isNull() const { return mReference == NULL || mReference->get() == NULL; }
|
bool isNull() const { return getPointer() == NULL; }
|
||||||
|
|
||||||
/// Returns true if the pointer is set.
|
|
||||||
[[nodiscard]] constexpr bool isValid() const { return mReference && mReference->get(); }
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T* operator->() const { return getPointer(); }
|
[[nodiscard]] constexpr T* operator->() const { return getPointer(); }
|
||||||
[[nodiscard]] constexpr T& operator*() const { return *getPointer(); }
|
[[nodiscard]] constexpr T& operator*() const { return *getPointer(); }
|
||||||
[[nodiscard]] constexpr operator T*() const { return getPointer(); }
|
[[nodiscard]] constexpr operator T*() const { return getPointer(); }
|
||||||
|
|
||||||
/// Returns the pointer.
|
/// Returns the pointer.
|
||||||
[[nodiscard]] constexpr T* getPointer() const { return mReference ? (T*)mReference->get() : NULL; }
|
[[nodiscard]] constexpr T* getPointer() const
|
||||||
|
{
|
||||||
|
auto ctrl = mWeak.lock();
|
||||||
|
if (!ctrl || !ctrl->object)
|
||||||
|
return NULL;
|
||||||
|
return (T*)(ctrl->object);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void set(WeakRefBase::WeakReference* ref)
|
void set(T* obj)
|
||||||
{
|
{
|
||||||
if (mReference)
|
if (!obj)
|
||||||
mReference->decRefCount();
|
|
||||||
mReference = NULL;
|
|
||||||
if (ref)
|
|
||||||
{
|
{
|
||||||
mReference = ref;
|
mWeak.reset();
|
||||||
mReference->incRefCount();
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(T* obj) { set(obj ? obj->getWeakReference() : NULL); }
|
mWeak = obj->getWeakControl();
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
template< typename > friend class SimObjectPtr;
|
template< typename > friend class SimObjectPtr;
|
||||||
WeakRefBase::WeakReference * mReference {NULL};
|
std::weak_ptr<WeakControlBlock> mWeak;
|
||||||
};
|
|
||||||
|
|
||||||
/// Union of an arbitrary type with a WeakRefBase. The exposed type will
|
|
||||||
/// remain accessible so long as the WeakRefBase is not cleared. Once
|
|
||||||
/// it is cleared, accessing the exposed type will result in a NULL pointer.
|
|
||||||
template<class ExposedType>
|
|
||||||
class WeakRefUnion
|
|
||||||
{
|
|
||||||
typedef WeakRefUnion<ExposedType> Union;
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr WeakRefUnion() : mPtr(NULL) {}
|
|
||||||
constexpr WeakRefUnion(const WeakRefPtr<WeakRefBase> & ref, ExposedType * ptr) : mPtr(NULL) { set(ref, ptr); }
|
|
||||||
constexpr WeakRefUnion(const Union & lock) : mPtr(NULL) { *this = lock; }
|
|
||||||
constexpr WeakRefUnion(WeakRefBase * ref) : mPtr(NULL) { set(ref, dynamic_cast<ExposedType*>(ref)); }
|
|
||||||
~WeakRefUnion() { mWeakReference = NULL; }
|
|
||||||
|
|
||||||
Union & operator=(const Union & ptr)
|
|
||||||
{
|
|
||||||
if (this == *ptr) { return *this; }
|
|
||||||
set(ptr.mWeakReference, ptr.getPointer());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(const WeakRefPtr<WeakRefBase> & ref, ExposedType * ptr)
|
|
||||||
{
|
|
||||||
mWeakReference = ref;
|
|
||||||
mPtr = ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool operator == (const ExposedType * t ) const { return getPointer() == t; }
|
|
||||||
[[nodiscard]] constexpr bool operator != (const ExposedType * t ) const { return getPointer() != t; }
|
|
||||||
[[nodiscard]] constexpr bool operator == (const Union &t ) const { return getPointer() == t.getPointer(); }
|
|
||||||
[[nodiscard]] constexpr bool operator != (const Union &t ) const { return getPointer() != t.getPointer(); }
|
|
||||||
[[nodiscard]] constexpr bool isNull() const { return mWeakReference.isNull() || !mPtr; }
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr ExposedType* getPointer() const { return !mWeakReference.isNull() ? mPtr : NULL; }
|
|
||||||
[[nodiscard]] constexpr ExposedType* operator->() const { return getPointer(); }
|
|
||||||
[[nodiscard]] constexpr ExposedType& operator*() const { return *getPointer(); }
|
|
||||||
[[nodiscard]] constexpr operator ExposedType*() const { return getPointer(); }
|
|
||||||
|
|
||||||
[[nodiscard]] WeakRefPtr<WeakRefBase> getRef() const { return mWeakReference; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
WeakRefPtr<WeakRefBase> mWeakReference;
|
|
||||||
ExposedType * mPtr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base class for objects which can be strongly referenced
|
/// Base class for objects which can be strongly referenced
|
||||||
|
|
@ -192,7 +140,12 @@ class StrongRefBase : public WeakRefBase
|
||||||
friend class StrongObjectRef;
|
friend class StrongObjectRef;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StrongRefBase() { mRefCount = 0; }
|
StrongRefBase()
|
||||||
|
{
|
||||||
|
mRefCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~StrongRefBase() = default;
|
||||||
|
|
||||||
U32 getRefCount() const { return mRefCount; }
|
U32 getRefCount() const { return mRefCount; }
|
||||||
|
|
||||||
|
|
@ -217,6 +170,7 @@ protected:
|
||||||
U32 mRefCount; ///< reference counter for StrongRefPtr objects
|
U32 mRefCount; ///< reference counter for StrongRefPtr objects
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Base class for StrongRefBase strong reference pointers.
|
/// Base class for StrongRefBase strong reference pointers.
|
||||||
class StrongObjectRef
|
class StrongObjectRef
|
||||||
{
|
{
|
||||||
|
|
@ -289,55 +243,6 @@ public:
|
||||||
T* getPointer() const { return const_cast<T*>(static_cast<T* const>(mObject)); }
|
T* getPointer() const { return const_cast<T*>(static_cast<T* const>(mObject)); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Union of an arbitrary type with a StrongRefBase. StrongRefBase will remain locked
|
|
||||||
/// until the union is destructed. Handy for when the exposed class will
|
|
||||||
/// become invalid whenever the reference becomes invalid and you want to make sure that doesn't
|
|
||||||
/// happen.
|
|
||||||
template<class ExposedType>
|
|
||||||
class StrongRefUnion
|
|
||||||
{
|
|
||||||
typedef StrongRefUnion<ExposedType> Union;
|
|
||||||
|
|
||||||
public:
|
|
||||||
StrongRefUnion() : mPtr(NULL) {}
|
|
||||||
|
|
||||||
StrongRefUnion(const StrongRefPtr<StrongRefBase> & ref, ExposedType * ptr) : mPtr(NULL) { set(ref, ptr); }
|
|
||||||
StrongRefUnion(const Union & lock) : mPtr(NULL) { *this = lock; }
|
|
||||||
StrongRefUnion(StrongRefBase * ref) : mPtr(NULL) { set(ref, dynamic_cast<ExposedType*>(ref)); }
|
|
||||||
|
|
||||||
~StrongRefUnion() { mReference = NULL; }
|
|
||||||
|
|
||||||
Union & operator=(const Union & ptr)
|
|
||||||
{
|
|
||||||
set(ptr.mReference, ptr.mPtr);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(const StrongRefPtr<StrongRefBase> & ref, ExposedType * ptr)
|
|
||||||
{
|
|
||||||
mReference = ref;
|
|
||||||
mPtr = ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool operator == (const ExposedType * t ) const { return mPtr == t; }
|
|
||||||
[[nodiscard]] constexpr bool operator != (const ExposedType * t ) const { return mPtr != t; }
|
|
||||||
[[nodiscard]] constexpr bool operator == (const Union &t ) const { return mPtr == t.mPtr; }
|
|
||||||
[[nodiscard]] constexpr bool operator != (const Union &t ) const { return mPtr != t.mPtr; }
|
|
||||||
[[nodiscard]] constexpr bool isNull() const { return mReference.isNull() || !mPtr; }
|
|
||||||
[[nodiscard]] constexpr bool isValid() const { return mReference.isValid() && mPtr; }
|
|
||||||
|
|
||||||
ExposedType* operator->() const { return mPtr; }
|
|
||||||
ExposedType& operator*() const { return *mPtr; }
|
|
||||||
operator ExposedType*() const { return mPtr; }
|
|
||||||
ExposedType* getPointer() const { return mPtr; }
|
|
||||||
|
|
||||||
StrongRefPtr<StrongRefBase> getRef() const { return mReference; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
StrongRefPtr<StrongRefBase> mReference;
|
|
||||||
ExposedType * mPtr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This oxymoron is a pointer that reference-counts the referenced
|
/// This oxymoron is a pointer that reference-counts the referenced
|
||||||
/// object but also NULLs out if the object goes away.
|
/// object but also NULLs out if the object goes away.
|
||||||
///
|
///
|
||||||
|
|
@ -351,93 +256,87 @@ template< class T >
|
||||||
class StrongWeakRefPtr
|
class StrongWeakRefPtr
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr StrongWeakRefPtr() : mReference( NULL ) {}
|
StrongWeakRefPtr() = default;
|
||||||
constexpr StrongWeakRefPtr( T* ptr ) : mReference( NULL ) { _set( ptr ); }
|
|
||||||
~StrongWeakRefPtr()
|
|
||||||
{
|
|
||||||
if( mReference )
|
|
||||||
{
|
|
||||||
T* ptr = _get();
|
|
||||||
if( ptr )
|
|
||||||
ptr->decRefCount();
|
|
||||||
|
|
||||||
mReference->decRefCount();
|
StrongWeakRefPtr(T* ptr) { set(ptr); }
|
||||||
}
|
|
||||||
|
StrongWeakRefPtr(const StrongWeakRefPtr& other)
|
||||||
|
{
|
||||||
|
set(other.getPointer());
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool isNull() const { return ( _get() == NULL ); }
|
StrongWeakRefPtr& operator=(const StrongWeakRefPtr& other)
|
||||||
[[nodiscard]] constexpr bool operator ==( T* ptr ) const { return ( _get() == ptr ); }
|
|
||||||
[[nodiscard]] constexpr bool operator !=( T* ptr ) const { return ( _get() != ptr ); }
|
|
||||||
[[nodiscard]] constexpr bool operator !() const { return isNull(); }
|
|
||||||
[[nodiscard]] constexpr T* operator ->() const { return _get(); }
|
|
||||||
[[nodiscard]] constexpr T& operator *() const { return *( _get() ); }
|
|
||||||
|
|
||||||
constexpr operator T*() const { return _get(); } // consider making this explicit
|
|
||||||
|
|
||||||
T* getPointer() const { return _get(); }
|
|
||||||
|
|
||||||
StrongWeakRefPtr& operator =( T* ptr )
|
|
||||||
{
|
{
|
||||||
_set( ptr );
|
if (this != &other)
|
||||||
|
set(other.getPointer());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~StrongWeakRefPtr()
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
StrongWeakRefPtr& operator=(T* ptr)
|
||||||
|
{
|
||||||
|
set(ptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValid() const { return getPointer() != NULL; }
|
||||||
|
bool isNull() const { return getPointer() == NULL; }
|
||||||
|
[[nodiscard]] bool operator==(T* ptr) const { return getPointer() == ptr; }
|
||||||
|
[[nodiscard]] bool operator!=(T* ptr) const { return getPointer() != ptr; }
|
||||||
|
[[nodiscard]] bool operator!() const { return isNull(); }
|
||||||
|
|
||||||
|
[[nodiscard]] T* operator->() const { return getPointer(); }
|
||||||
|
[[nodiscard]] T& operator*() const { return *getPointer(); }
|
||||||
|
constexpr operator T* () const { return getPointer(); }
|
||||||
|
|
||||||
|
[[nodiscard]] T* getPointer() const
|
||||||
|
{
|
||||||
|
return mControl ? static_cast<T*>(mControl->object) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WeakRefBase::WeakReference* mReference;
|
std::shared_ptr<WeakControlBlock> mControl;
|
||||||
|
T* mPtr = NULL;
|
||||||
|
|
||||||
T* _get() const
|
void set(T* ptr)
|
||||||
{
|
{
|
||||||
if( mReference )
|
release();
|
||||||
return static_cast< T* >( mReference->get() );
|
if (!ptr) return;
|
||||||
else
|
|
||||||
return NULL;
|
// Always hold the identity
|
||||||
|
mControl = ptr->getWeakControl().lock();
|
||||||
|
if (!mControl) return;
|
||||||
|
|
||||||
|
mPtr = ptr;
|
||||||
|
|
||||||
|
// Conditionally retain object lifetime if T supports intrusive refcount
|
||||||
|
// runtime check: only strong ref if T inherits StrongRefBase
|
||||||
|
if constexpr (std::is_base_of_v<StrongRefBase, T>)
|
||||||
|
{
|
||||||
|
mPtr->incRefCount();
|
||||||
}
|
}
|
||||||
void _set( T* ptr )
|
|
||||||
{
|
|
||||||
if( mReference )
|
|
||||||
{
|
|
||||||
T* old = _get();
|
|
||||||
if( old )
|
|
||||||
old->decRefCount();
|
|
||||||
|
|
||||||
mReference->decRefCount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ptr )
|
void release()
|
||||||
{
|
{
|
||||||
ptr->incRefCount();
|
if (mPtr)
|
||||||
mReference = ptr->getWeakReference();
|
{
|
||||||
mReference->incRefCount();
|
if constexpr (std::is_base_of_v<StrongRefBase, T>)
|
||||||
|
{
|
||||||
|
mPtr->decRefCount();
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
mReference = NULL;
|
|
||||||
|
mPtr = NULL;
|
||||||
|
mControl.reset();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
|
|
||||||
inline void WeakRefBase::clearWeakReferences()
|
|
||||||
{
|
|
||||||
if (mReference)
|
|
||||||
{
|
|
||||||
mReference->mObject = NULL;
|
|
||||||
mReference->decRefCount();
|
|
||||||
mReference = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline WeakRefBase::WeakReference* WeakRefBase::getWeakReference()
|
|
||||||
{
|
|
||||||
if (!mReference)
|
|
||||||
{
|
|
||||||
mReference = new WeakReference(this);
|
|
||||||
mReference->incRefCount();
|
|
||||||
}
|
|
||||||
return mReference;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
struct TypeTraits< WeakRefPtr< T > > : public _TypeTraits< WeakRefPtr< T > >
|
struct TypeTraits< WeakRefPtr< T > > : public _TypeTraits< WeakRefPtr< T > >
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1094,7 +1094,7 @@ void ScatterSky::_render( ObjectRenderInst *ri, SceneRenderState *state, BaseMat
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GFX->setCubeTexture( 0, NULL );
|
GFX->setTexture( 0, NULL );
|
||||||
mShaderConsts->setSafe( mUseCubemapSC, 0.0f );
|
mShaderConsts->setSafe( mUseCubemapSC, 0.0f );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,15 +41,16 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static bool sReadPNG(const Torque::Path& path, GBitmap* bitmap);
|
|
||||||
static bool sReadStreamPNG(Stream& stream, GBitmap* bitmap, U32 len);
|
|
||||||
|
|
||||||
/// Compression levels for PNGs range from 0-9.
|
/// Compression levels for PNGs range from 0-9.
|
||||||
/// A value outside that range will cause the write routine to look for the best compression for a given PNG. This can be slow.
|
/// A value outside that range will cause the write routine to look for the best compression for a given PNG. This can be slow.
|
||||||
|
static bool sReadPNG(const Torque::Path& path, GBitmap* bitmap);
|
||||||
|
static bool sReadStreamPNG(Stream& stream, GBitmap* bitmap, U32 len);
|
||||||
static bool sWritePNG(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel);
|
static bool sWritePNG(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel);
|
||||||
static bool sWriteStreamPNG(const String& bmType, Stream& stream, GBitmap* bitmap, U32 compressionLevel);
|
static bool sWriteStreamPNG(const String& bmType, Stream& stream, GBitmap* bitmap, U32 compressionLevel);
|
||||||
static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter);
|
|
||||||
|
// Internal functions used.
|
||||||
|
static bool _readStreamPNG(Stream& stream, GBitmap* bitmap, U32 len, const char* filename);
|
||||||
|
static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter, const char* filename = "unknown");
|
||||||
|
|
||||||
static struct _privateRegisterPNG
|
static struct _privateRegisterPNG
|
||||||
{
|
{
|
||||||
|
|
@ -72,421 +73,390 @@ static struct _privateRegisterPNG
|
||||||
}
|
}
|
||||||
} sStaticRegisterPNG;
|
} sStaticRegisterPNG;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
//-------------------------------------- Replacement I/O for standard LIBPng
|
// PNG context passed to libpng for error reporting
|
||||||
// functions. we don't wanna use
|
//-----------------------------------------------------------------------------
|
||||||
// FILE*'s...
|
struct PngContext
|
||||||
static void pngReadDataFn(png_structp png_ptr,
|
|
||||||
png_bytep data,
|
|
||||||
png_size_t length)
|
|
||||||
{
|
{
|
||||||
AssertFatal(png_get_io_ptr(png_ptr) != NULL, "No stream?");
|
const char* filename;
|
||||||
|
explicit PngContext(const char* f) : filename(f) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// libpng callbacks
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void pngReadDataFn(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||||
|
{
|
||||||
Stream* strm = (Stream*)png_get_io_ptr(png_ptr);
|
Stream* strm = (Stream*)png_get_io_ptr(png_ptr);
|
||||||
bool success = strm->read(length, data);
|
AssertFatal(strm, "pngReadDataFn - No stream.");
|
||||||
AssertFatal(success, "pngReadDataFn - failed to read from stream!");
|
if (!strm->read(length, data))
|
||||||
|
Con::errorf("pngReadDataFn - Failed to read %u bytes from stream.", (U32)length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pngWriteDataFn(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||||
//--------------------------------------
|
|
||||||
static void pngWriteDataFn(png_structp png_ptr,
|
|
||||||
png_bytep data,
|
|
||||||
png_size_t length)
|
|
||||||
{
|
{
|
||||||
AssertFatal(png_get_io_ptr(png_ptr) != NULL, "No stream?");
|
|
||||||
|
|
||||||
Stream* strm = (Stream*)png_get_io_ptr(png_ptr);
|
Stream* strm = (Stream*)png_get_io_ptr(png_ptr);
|
||||||
bool success = strm->write(length, data);
|
AssertFatal(strm, "pngWriteDataFn - No stream.");
|
||||||
AssertFatal(success, "pngWriteDataFn - failed to write to stream!");
|
if (!strm->write(length, data))
|
||||||
|
Con::errorf("pngWriteDataFn - Failed to write %u bytes to stream.", (U32)length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pngFlushDataFn(png_structp /*png_ptr*/) {}
|
||||||
|
|
||||||
//--------------------------------------
|
static png_voidp pngMallocFn(png_structp /*png_ptr*/, png_size_t size) { return FrameAllocator::alloc(size); }
|
||||||
static void pngFlushDataFn(png_structp /*png_ptr*/)
|
static void pngFreeFn(png_structp /*png_ptr*/, png_voidp /*mem*/) {}
|
||||||
|
|
||||||
|
static png_voidp pngRealMallocFn(png_structp /*png_ptr*/, png_size_t size) { return (png_voidp)dMalloc(size); }
|
||||||
|
static void pngRealFreeFn(png_structp /*png_ptr*/, png_voidp mem) { dFree(mem); }
|
||||||
|
|
||||||
|
static void pngFatalErrorFn(png_structp png_ptr, png_const_charp pMessage)
|
||||||
{
|
{
|
||||||
//
|
const char* filename = "unknown";
|
||||||
|
if (png_ptr)
|
||||||
|
{
|
||||||
|
PngContext* ctx = (PngContext*)png_get_error_ptr(png_ptr);
|
||||||
|
if (ctx && ctx->filename)
|
||||||
|
filename = ctx->filename;
|
||||||
|
}
|
||||||
|
Con::errorf("pngFatalErrorFn - Fatal error in '%s': %s", filename, pMessage);
|
||||||
|
AssertISV(false, avar("libpng fatal error in '%s':\n%s", filename, pMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
static png_voidp pngMallocFn(png_structp /*png_ptr*/, png_size_t size)
|
static void pngWarningFn(png_structp png_ptr, png_const_charp pMessage)
|
||||||
{
|
{
|
||||||
return FrameAllocator::alloc(size);
|
#if TORQUE_DEBUG
|
||||||
|
const char* filename = "unknown";
|
||||||
|
if (png_ptr)
|
||||||
|
{
|
||||||
|
PngContext* ctx = (PngContext*)png_get_error_ptr(png_ptr);
|
||||||
|
if (ctx && ctx->filename)
|
||||||
|
filename = ctx->filename;
|
||||||
|
}
|
||||||
|
Con::warnf("pngWarningFn - Warning in '%s': %s", filename, pMessage);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pngFreeFn(png_structp /*png_ptr*/, png_voidp /*mem*/)
|
//-----------------------------------------------------------------------------
|
||||||
|
// RAII guards for png read/write structs
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
struct PngReadGuard
|
||||||
{
|
{
|
||||||
}
|
png_structp png_ptr = nullptr;
|
||||||
|
png_infop info_ptr = nullptr;
|
||||||
|
png_infop end_info = nullptr;
|
||||||
|
PngContext ctx;
|
||||||
|
|
||||||
static png_voidp pngRealMallocFn(png_structp /*png_ptr*/, png_size_t size)
|
explicit PngReadGuard(const char* filename) : ctx(filename) {}
|
||||||
|
|
||||||
|
bool init()
|
||||||
{
|
{
|
||||||
return (png_voidp)dMalloc(size);
|
png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
|
||||||
}
|
&ctx, pngFatalErrorFn, pngWarningFn,
|
||||||
|
NULL, pngRealMallocFn, pngRealFreeFn);
|
||||||
static void pngRealFreeFn(png_structp /*png_ptr*/, png_voidp mem)
|
if (!png_ptr)
|
||||||
{
|
{
|
||||||
dFree(mem);
|
Con::errorf("PngReadGuard - png_create_read_struct_2 failed for '%s'.", ctx.filename);
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------
|
|
||||||
static void pngFatalErrorFn(png_structp /*png_ptr*/,
|
|
||||||
png_const_charp pMessage)
|
|
||||||
{
|
|
||||||
AssertISV(false, avar("Error reading PNG file:\n %s", pMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------
|
|
||||||
static void pngWarningFn(png_structp, png_const_charp /*pMessage*/)
|
|
||||||
{
|
|
||||||
// AssertWarn(false, avar("Warning reading PNG file:\n %s", pMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------
|
|
||||||
bool sReadPNG(const Torque::Path& path, GBitmap* bitmap)
|
|
||||||
{
|
|
||||||
FileStream* readPng = new FileStream;
|
|
||||||
|
|
||||||
if (!readPng->open(path.getFullPath(), Torque::FS::File::Read))
|
|
||||||
{
|
|
||||||
Con::printf("Failed to open PNG :%s", path.getFullFileName().c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sReadStreamPNG(*readPng, bitmap, U32_MAX))
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if (!info_ptr)
|
||||||
{
|
{
|
||||||
Con::printf("Failed to read PNG :%s", path.getFullFileName().c_str());
|
Con::errorf("PngReadGuard - png_create_info_struct (info) failed for '%s'.", ctx.filename);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
readPng->close();
|
end_info = png_create_info_struct(png_ptr);
|
||||||
|
if (!end_info)
|
||||||
|
{
|
||||||
|
Con::errorf("PngReadGuard - png_create_info_struct (end) failed for '%s'.", ctx.filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sReadStreamPNG(Stream& stream, GBitmap* bitmap, U32 len)
|
~PngReadGuard()
|
||||||
{
|
{
|
||||||
PROFILE_SCOPE(sReadPNG);
|
if (png_ptr)
|
||||||
static const U32 cs_headerBytesChecked = 8;
|
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
||||||
|
|
||||||
U8 header[cs_headerBytesChecked];
|
|
||||||
stream.read(cs_headerBytesChecked, header);
|
|
||||||
|
|
||||||
bool isPng = png_check_sig(header, cs_headerBytesChecked) != 0;
|
|
||||||
if (isPng == false)
|
|
||||||
{
|
|
||||||
AssertWarn(false, "GBitmap::readPNG: stream doesn't contain a PNG");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
U32 prevWaterMark = FrameAllocator::getWaterMark();
|
struct PngWriteGuard
|
||||||
png_structp png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
|
{
|
||||||
|
png_structp png_ptr = nullptr;
|
||||||
|
png_infop info_ptr = nullptr;
|
||||||
|
PngContext ctx;
|
||||||
|
|
||||||
|
explicit PngWriteGuard(const char* filename) : ctx(filename) {}
|
||||||
|
|
||||||
|
bool init(bool useFrameAllocator = true)
|
||||||
|
{
|
||||||
|
png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
|
||||||
|
&ctx, pngFatalErrorFn, pngWarningFn,
|
||||||
NULL,
|
NULL,
|
||||||
pngFatalErrorFn,
|
useFrameAllocator ? pngMallocFn : pngRealMallocFn,
|
||||||
pngWarningFn,
|
useFrameAllocator ? pngFreeFn : pngRealFreeFn);
|
||||||
NULL,
|
if (!png_ptr)
|
||||||
pngRealMallocFn,
|
|
||||||
pngRealFreeFn);
|
|
||||||
|
|
||||||
if (png_ptr == NULL)
|
|
||||||
{
|
{
|
||||||
FrameAllocator::setWaterMark(prevWaterMark);
|
Con::errorf("PngWriteGuard - png_create_write_struct_2 failed for '%s'.", ctx.filename);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
if (info_ptr == NULL)
|
if (!info_ptr)
|
||||||
{
|
{
|
||||||
png_destroy_read_struct(&png_ptr,
|
Con::errorf("PngWriteGuard - png_create_info_struct failed for '%s'.", ctx.filename);
|
||||||
(png_infopp)NULL,
|
|
||||||
(png_infopp)NULL);
|
|
||||||
|
|
||||||
FrameAllocator::setWaterMark(prevWaterMark);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
png_infop end_info = png_create_info_struct(png_ptr);
|
return true;
|
||||||
if (end_info == NULL)
|
|
||||||
{
|
|
||||||
png_destroy_read_struct(&png_ptr,
|
|
||||||
&info_ptr,
|
|
||||||
(png_infopp)NULL);
|
|
||||||
|
|
||||||
FrameAllocator::setWaterMark(prevWaterMark);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
png_set_read_fn(png_ptr, &stream, pngReadDataFn);
|
~PngWriteGuard()
|
||||||
|
{
|
||||||
|
if (png_ptr)
|
||||||
|
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Read off the info on the image.
|
//-----------------------------------------------------------------------------
|
||||||
png_set_sig_bytes(png_ptr, cs_headerBytesChecked);
|
// Shared helpers
|
||||||
png_read_info(png_ptr, info_ptr);
|
//-----------------------------------------------------------------------------
|
||||||
|
static GFXFormat _determineReadFormat(png_structp png_ptr, png_infop info_ptr, bool& transAlpha)
|
||||||
|
{
|
||||||
|
S32 bit_depth, color_type;
|
||||||
|
png_uint_32 width, height;
|
||||||
|
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
|
||||||
|
|
||||||
// OK, at this point, if we have reached it ok, then we can reset the
|
transAlpha = false;
|
||||||
// image to accept the new data...
|
|
||||||
//
|
|
||||||
bitmap->deleteImage();
|
|
||||||
|
|
||||||
png_uint_32 width;
|
|
||||||
png_uint_32 height;
|
|
||||||
S32 bit_depth;
|
|
||||||
S32 color_type;
|
|
||||||
|
|
||||||
png_get_IHDR(png_ptr, info_ptr,
|
|
||||||
&width, &height, // obv.
|
|
||||||
&bit_depth, &color_type, // obv.
|
|
||||||
NULL, // interlace
|
|
||||||
NULL, // compression_type
|
|
||||||
NULL); // filter_type
|
|
||||||
|
|
||||||
// First, handle the color transformations. We need this to read in the
|
|
||||||
// data as RGB or RGBA, _always_, with a maximal channel width of 8 bits.
|
|
||||||
//
|
|
||||||
bool transAlpha = false;
|
|
||||||
GFXFormat format = GFXFormatR8G8B8;
|
GFXFormat format = GFXFormatR8G8B8;
|
||||||
|
|
||||||
// Strip off any 16 bit info
|
|
||||||
//
|
|
||||||
if (bit_depth == 16 && color_type != PNG_COLOR_TYPE_GRAY)
|
if (bit_depth == 16 && color_type != PNG_COLOR_TYPE_GRAY)
|
||||||
{
|
|
||||||
png_set_strip_16(png_ptr);
|
png_set_strip_16(png_ptr);
|
||||||
}
|
|
||||||
|
|
||||||
// Expand a transparency channel into a full alpha channel...
|
|
||||||
//
|
|
||||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
||||||
{
|
{
|
||||||
png_set_expand(png_ptr);
|
png_set_expand(png_ptr);
|
||||||
transAlpha = true;
|
transAlpha = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
switch (color_type)
|
||||||
{
|
{
|
||||||
|
case PNG_COLOR_TYPE_PALETTE:
|
||||||
png_set_expand(png_ptr);
|
png_set_expand(png_ptr);
|
||||||
format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
|
format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
|
||||||
}
|
break;
|
||||||
else if (color_type == PNG_COLOR_TYPE_GRAY)
|
case PNG_COLOR_TYPE_GRAY:
|
||||||
{
|
|
||||||
png_set_expand(png_ptr);
|
png_set_expand(png_ptr);
|
||||||
|
format = (bit_depth == 16) ? GFXFormatR5G6B5 : GFXFormatA8;
|
||||||
if (bit_depth == 16)
|
break;
|
||||||
format = GFXFormatR5G6B5;
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
||||||
else
|
|
||||||
format = GFXFormatA8;
|
|
||||||
}
|
|
||||||
else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
|
||||||
{
|
|
||||||
png_set_expand(png_ptr);
|
png_set_expand(png_ptr);
|
||||||
png_set_gray_to_rgb(png_ptr);
|
png_set_gray_to_rgb(png_ptr);
|
||||||
format = GFXFormatR8G8B8A8;
|
format = GFXFormatR8G8B8A8;
|
||||||
}
|
break;
|
||||||
else if (color_type == PNG_COLOR_TYPE_RGB)
|
case PNG_COLOR_TYPE_RGB:
|
||||||
{
|
|
||||||
format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
|
|
||||||
png_set_expand(png_ptr);
|
png_set_expand(png_ptr);
|
||||||
}
|
format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
|
||||||
else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
break;
|
||||||
{
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||||
png_set_expand(png_ptr);
|
png_set_expand(png_ptr);
|
||||||
format = GFXFormatR8G8B8A8;
|
format = GFXFormatR8G8B8A8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Con::errorf("_determineReadFormat - Unrecognised color_type %d.", color_type);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the info pointer with the result of the transformations
|
return format;
|
||||||
// above...
|
}
|
||||||
png_read_update_info(png_ptr, info_ptr);
|
|
||||||
|
|
||||||
png_uint_32 rowBytes = png_get_rowbytes(png_ptr, info_ptr);
|
static bool _validateRowBytes(const char* filename, GFXFormat format, U32 rowBytes, U32 width)
|
||||||
if (format == GFXFormatR8G8B8)
|
|
||||||
{
|
{
|
||||||
AssertFatal(rowBytes == width * 3,
|
const U32 expected =
|
||||||
"Error, our rowbytes are incorrect for this transform... (3)");
|
(format == GFXFormatR8G8B8) ? width * 3 :
|
||||||
}
|
(format == GFXFormatR8G8B8A8) ? width * 4 :
|
||||||
else if (format == GFXFormatR8G8B8A8)
|
(format == GFXFormatR5G6B5) ? width * 2 : 0;
|
||||||
|
|
||||||
|
if (expected && rowBytes != expected)
|
||||||
{
|
{
|
||||||
AssertFatal(rowBytes == width * 4,
|
Con::errorf("_validateRowBytes - '%s': rowBytes %d != expected %d for format %d.",
|
||||||
"Error, our rowbytes are incorrect for this transform... (4)");
|
filename, rowBytes, expected, format);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else if (format == GFXFormatR5G6B5)
|
|
||||||
{
|
|
||||||
AssertFatal(rowBytes == width * 2,
|
|
||||||
"Error, our rowbytes are incorrect for this transform... (2)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// actually allocate the bitmap space...
|
|
||||||
bitmap->allocateBitmap(width, height,
|
|
||||||
false, // don't extrude miplevels...
|
|
||||||
format); // use determined format...
|
|
||||||
|
|
||||||
// Set up the row pointers...
|
|
||||||
png_bytep* rowPointers = new png_bytep[ height ];
|
|
||||||
U8* pBase = (U8*)bitmap->getBits();
|
|
||||||
|
|
||||||
for (U32 i = 0; i < height; i++)
|
|
||||||
rowPointers[i] = pBase + (i * rowBytes);
|
|
||||||
|
|
||||||
// And actually read the image!
|
|
||||||
png_read_image(png_ptr, rowPointers);
|
|
||||||
|
|
||||||
// We're outta here, destroy the png structs, and release the lock
|
|
||||||
// as quickly as possible...
|
|
||||||
//png_read_end(png_ptr, end_info);
|
|
||||||
delete [] rowPointers;
|
|
||||||
png_read_end(png_ptr, NULL);
|
|
||||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
|
||||||
|
|
||||||
// Ok, the image is read in, now we need to finish up the initialization,
|
|
||||||
// which means: setting up the detailing members, init'ing the palette
|
|
||||||
// key, etc...
|
|
||||||
//
|
|
||||||
// actually, all of that was handled by allocateBitmap, so we're outta here
|
|
||||||
//
|
|
||||||
|
|
||||||
// Check this bitmap for transparency
|
|
||||||
bitmap->checkForTransparency();
|
|
||||||
|
|
||||||
FrameAllocator::setWaterMark(prevWaterMark);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
static void _applyWriteIHDR(png_structp png_ptr, png_infop info_ptr, GFXFormat format, U32 width, U32 height)
|
||||||
static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter)
|
|
||||||
{
|
{
|
||||||
GFXFormat format = bitmap->getFormat();
|
switch (format)
|
||||||
|
|
||||||
// ONLY RGB bitmap writing supported at this time!
|
|
||||||
AssertFatal( format == GFXFormatR8G8B8 ||
|
|
||||||
format == GFXFormatR8G8B8A8 ||
|
|
||||||
format == GFXFormatR8G8B8X8 ||
|
|
||||||
format == GFXFormatA8 ||
|
|
||||||
format == GFXFormatR5G6B5 ||
|
|
||||||
format == GFXFormatR8G8B8A8_LINEAR_FORCE, "_writePNG: ONLY RGB bitmap writing supported at this time.");
|
|
||||||
|
|
||||||
if ( format != GFXFormatR8G8B8 &&
|
|
||||||
format != GFXFormatR8G8B8A8 &&
|
|
||||||
format != GFXFormatR8G8B8X8 &&
|
|
||||||
format != GFXFormatA8 &&
|
|
||||||
format != GFXFormatR5G6B5 && format != GFXFormatR8G8B8A8_LINEAR_FORCE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
|
|
||||||
NULL,
|
|
||||||
pngFatalErrorFn,
|
|
||||||
pngWarningFn,
|
|
||||||
NULL,
|
|
||||||
pngMallocFn,
|
|
||||||
pngFreeFn);
|
|
||||||
if (png_ptr == NULL)
|
|
||||||
return (false);
|
|
||||||
|
|
||||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
|
||||||
if (info_ptr == NULL)
|
|
||||||
{
|
{
|
||||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
case GFXFormatR8G8B8:
|
||||||
return false;
|
png_set_IHDR(png_ptr, info_ptr, width, height, 8,
|
||||||
}
|
PNG_COLOR_TYPE_RGB, NULL, NULL, NULL);
|
||||||
|
break;
|
||||||
png_set_write_fn(png_ptr, &stream, pngWriteDataFn, pngFlushDataFn);
|
case GFXFormatR8G8B8A8:
|
||||||
|
case GFXFormatR8G8B8X8:
|
||||||
// Set the compression level and image filters
|
case GFXFormatR8G8B8A8_LINEAR_FORCE:
|
||||||
png_set_compression_window_bits(png_ptr, 15);
|
png_set_IHDR(png_ptr, info_ptr, width, height, 8,
|
||||||
png_set_compression_level(png_ptr, compressionLevel);
|
PNG_COLOR_TYPE_RGB_ALPHA, NULL, NULL, NULL);
|
||||||
png_set_filter(png_ptr, 0, filter);
|
break;
|
||||||
|
case GFXFormatA8:
|
||||||
// Set the image information here. Width and height are up to 2^31,
|
png_set_IHDR(png_ptr, info_ptr, width, height, 8,
|
||||||
// bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
|
PNG_COLOR_TYPE_GRAY, NULL, NULL, NULL);
|
||||||
// the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
|
break;
|
||||||
// PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
|
case GFXFormatR5G6B5:
|
||||||
// or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
|
|
||||||
// PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
|
|
||||||
// currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
|
|
||||||
|
|
||||||
U32 width = bitmap->getWidth();
|
|
||||||
U32 height = bitmap->getHeight();
|
|
||||||
|
|
||||||
if (format == GFXFormatR8G8B8)
|
|
||||||
{
|
{
|
||||||
png_set_IHDR(png_ptr, info_ptr,
|
png_set_IHDR(png_ptr, info_ptr, width, height, 16,
|
||||||
width, height, // the width & height
|
PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
|
||||||
8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type,
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
NULL, // no interlace
|
|
||||||
NULL, // compression type
|
|
||||||
NULL); // filter type
|
|
||||||
}
|
|
||||||
else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatR8G8B8A8_LINEAR_FORCE)
|
|
||||||
{
|
|
||||||
png_set_IHDR(png_ptr, info_ptr,
|
|
||||||
width, height, // the width & height
|
|
||||||
8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type,
|
|
||||||
NULL, // no interlace
|
|
||||||
NULL, // compression type
|
|
||||||
NULL); // filter type
|
|
||||||
}
|
|
||||||
else if (format == GFXFormatA8)
|
|
||||||
{
|
|
||||||
png_set_IHDR(png_ptr, info_ptr,
|
|
||||||
width, height, // the width & height
|
|
||||||
8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
|
||||||
NULL, // no interlace
|
|
||||||
NULL, // compression type
|
|
||||||
NULL); // filter type
|
|
||||||
}
|
|
||||||
else if (format == GFXFormatR5G6B5)
|
|
||||||
{
|
|
||||||
png_set_IHDR(png_ptr, info_ptr,
|
|
||||||
width, height, // the width & height
|
|
||||||
16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
|
||||||
PNG_INTERLACE_NONE, // no interlace
|
|
||||||
PNG_COMPRESSION_TYPE_DEFAULT, // compression type
|
|
||||||
PNG_FILTER_TYPE_DEFAULT); // filter type
|
|
||||||
|
|
||||||
png_color_8_struct sigBit = { 0 };
|
png_color_8_struct sigBit = { 0 };
|
||||||
sigBit.gray = 16;
|
sigBit.gray = 16;
|
||||||
png_set_sBIT(png_ptr, info_ptr, &sigBit);
|
png_set_sBIT(png_ptr, info_ptr, &sigBit);
|
||||||
|
|
||||||
png_set_swap(png_ptr);
|
png_set_swap(png_ptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
png_write_info(png_ptr, info_ptr);
|
static bool _isSupportedWriteFormat(GFXFormat format)
|
||||||
FrameAllocatorMarker marker;
|
{
|
||||||
png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) );
|
switch (format)
|
||||||
|
{
|
||||||
|
case GFXFormatR8G8B8:
|
||||||
|
case GFXFormatR8G8B8A8:
|
||||||
|
case GFXFormatR8G8B8X8:
|
||||||
|
case GFXFormatR8G8B8A8_LINEAR_FORCE:
|
||||||
|
case GFXFormatA8:
|
||||||
|
case GFXFormatR5G6B5:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Core read / write
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static bool _readStreamPNG(Stream& stream, GBitmap* bitmap, U32 /*len*/, const char* filename = "unknown")
|
||||||
|
{
|
||||||
|
PROFILE_SCOPE(sReadPNG);
|
||||||
|
|
||||||
|
static const U32 cs_headerBytesChecked = 8;
|
||||||
|
U8 header[cs_headerBytesChecked];
|
||||||
|
stream.read(cs_headerBytesChecked, header);
|
||||||
|
|
||||||
|
if (!png_check_sig(header, cs_headerBytesChecked))
|
||||||
|
{
|
||||||
|
Con::errorf("_readStreamPNG - '%s' does not have a valid PNG signature.", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 prevWaterMark = FrameAllocator::getWaterMark();
|
||||||
|
|
||||||
|
PngReadGuard guard(filename);
|
||||||
|
if (!guard.init())
|
||||||
|
{
|
||||||
|
FrameAllocator::setWaterMark(prevWaterMark);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_set_read_fn(guard.png_ptr, &stream, pngReadDataFn);
|
||||||
|
png_set_sig_bytes(guard.png_ptr, cs_headerBytesChecked);
|
||||||
|
png_read_info(guard.png_ptr, guard.info_ptr);
|
||||||
|
|
||||||
|
bool transAlpha = false;
|
||||||
|
GFXFormat format = _determineReadFormat(guard.png_ptr, guard.info_ptr, transAlpha);
|
||||||
|
|
||||||
|
png_uint_32 number_of_passes = png_set_interlace_handling(guard.png_ptr);
|
||||||
|
png_read_update_info(guard.png_ptr, guard.info_ptr);
|
||||||
|
|
||||||
|
png_uint_32 width = png_get_image_width(guard.png_ptr, guard.info_ptr);
|
||||||
|
png_uint_32 height = png_get_image_height(guard.png_ptr, guard.info_ptr);
|
||||||
|
png_uint_32 rowBytes = png_get_rowbytes(guard.png_ptr, guard.info_ptr);
|
||||||
|
|
||||||
|
if (!_validateRowBytes(filename, format, rowBytes, width))
|
||||||
|
{
|
||||||
|
FrameAllocator::setWaterMark(prevWaterMark);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap->deleteImage();
|
||||||
|
bitmap->allocateBitmap(width, height, false, format);
|
||||||
|
|
||||||
|
png_bytep* rowPointers = new png_bytep[height];
|
||||||
|
U8* pBase = (U8*)bitmap->getBits();
|
||||||
for (U32 i = 0; i < height; i++)
|
for (U32 i = 0; i < height; i++)
|
||||||
row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i));
|
rowPointers[i] = pBase + (i * rowBytes);
|
||||||
|
|
||||||
png_write_image(png_ptr, row_pointers);
|
png_read_image(guard.png_ptr, rowPointers);
|
||||||
|
delete[] rowPointers;
|
||||||
|
|
||||||
// Write S3TC data if present...
|
png_read_end(guard.png_ptr, NULL);
|
||||||
// Write FXT1 data if present...
|
|
||||||
|
|
||||||
png_write_end(png_ptr, info_ptr);
|
|
||||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
|
||||||
|
|
||||||
|
bitmap->checkForTransparency();
|
||||||
|
FrameAllocator::setWaterMark(prevWaterMark);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool _writePNG(GBitmap* bitmap, Stream& stream, U32 compressionLevel, U32 strategy, U32 filter, const char* filename)
|
||||||
//--------------------------------------------------------------------------
|
|
||||||
bool sWritePNG(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel)
|
|
||||||
{
|
{
|
||||||
FileStream* writePng = new FileStream;
|
GFXFormat format = bitmap->getFormat();
|
||||||
|
|
||||||
if (!writePng->open(path.getFullPath(), Torque::FS::File::Write))
|
if (!_isSupportedWriteFormat(format))
|
||||||
{
|
{
|
||||||
Con::printf("Failed to open PNG :%s", path.getFullFileName().c_str());
|
Con::errorf("_writePNG - '%s': Unsupported format %d.", filename, format);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sWriteStreamPNG("png", *writePng, bitmap, compressionLevel))
|
PngWriteGuard guard(filename);
|
||||||
{
|
if (!guard.init(true))
|
||||||
Con::printf("Failed to write PNG :%s", path.getFullFileName().c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
writePng->close();
|
png_set_write_fn(guard.png_ptr, &stream, pngWriteDataFn, pngFlushDataFn);
|
||||||
|
png_set_compression_window_bits(guard.png_ptr, 15);
|
||||||
|
png_set_compression_level(guard.png_ptr, compressionLevel);
|
||||||
|
png_set_filter(guard.png_ptr, 0, filter);
|
||||||
|
|
||||||
|
_applyWriteIHDR(guard.png_ptr, guard.info_ptr, format, bitmap->getWidth(), bitmap->getHeight());
|
||||||
|
png_write_info(guard.png_ptr, guard.info_ptr);
|
||||||
|
|
||||||
|
FrameAllocatorMarker marker;
|
||||||
|
const U32 height = bitmap->getHeight();
|
||||||
|
png_bytep* rowPointers = (png_bytep*)marker.alloc(height * sizeof(png_bytep));
|
||||||
|
for (U32 i = 0; i < height; i++)
|
||||||
|
rowPointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i));
|
||||||
|
|
||||||
|
png_write_image(guard.png_ptr, rowPointers);
|
||||||
|
png_write_end(guard.png_ptr, guard.info_ptr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sWriteStreamPNG(const String& bmType, Stream& stream, GBitmap* bitmap, U32 compressionLevel)
|
//-----------------------------------------------------------------------------
|
||||||
|
// Public interface
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static bool sReadPNG(const Torque::Path& path, GBitmap* bitmap)
|
||||||
|
{
|
||||||
|
FileStream readPng;
|
||||||
|
if (!readPng.open(path.getFullPath(), Torque::FS::File::Read))
|
||||||
|
{
|
||||||
|
Con::errorf("sReadPNG - Failed to open '%s'.", path.getFullPath().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = _readStreamPNG(readPng, bitmap, U32_MAX, path.getFullPath().c_str());
|
||||||
|
if (!result)
|
||||||
|
Con::errorf("sReadPNG - Failed to decode '%s'.", path.getFullPath().c_str());
|
||||||
|
|
||||||
|
readPng.close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sWriteStreamPNG(const String& /*bmType*/, Stream& stream, GBitmap* bitmap, U32 compressionLevel)
|
||||||
{
|
{
|
||||||
U32 waterMark = FrameAllocator::getWaterMark();
|
U32 waterMark = FrameAllocator::getWaterMark();
|
||||||
|
|
||||||
|
|
@ -501,19 +471,11 @@ static bool sWriteStreamPNG(const String& bmType, Stream& stream, GBitmap* bitma
|
||||||
U8* buffer = new U8[1 << 22]; // 4 Megs. Should be enough...
|
U8* buffer = new U8[1 << 22]; // 4 Megs. Should be enough...
|
||||||
MemStream* pMemStream = new MemStream(1 << 22, buffer, false, true);
|
MemStream* pMemStream = new MemStream(1 << 22, buffer, false, true);
|
||||||
|
|
||||||
const U32 zStrategies[] = { Z_DEFAULT_STRATEGY,
|
const U32 zStrategies[] = { Z_DEFAULT_STRATEGY, Z_FILTERED };
|
||||||
Z_FILTERED };
|
const U32 pngFilters[] = { PNG_FILTER_NONE, PNG_FILTER_SUB, PNG_FILTER_UP,
|
||||||
const U32 pngFilters[] = { PNG_FILTER_NONE,
|
PNG_FILTER_AVG, PNG_FILTER_PAETH, PNG_ALL_FILTERS };
|
||||||
PNG_FILTER_SUB,
|
|
||||||
PNG_FILTER_UP,
|
|
||||||
PNG_FILTER_AVG,
|
|
||||||
PNG_FILTER_PAETH,
|
|
||||||
PNG_ALL_FILTERS };
|
|
||||||
|
|
||||||
U32 minSize = 0xFFFFFFFF;
|
U32 minSize = U32_MAX, bestCLevel = 0, bestStrategy = 0, bestFilter = 0;
|
||||||
U32 bestStrategy = 0xFFFFFFFF;
|
|
||||||
U32 bestFilter = 0xFFFFFFFF;
|
|
||||||
U32 bestCLevel = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
for (U32 cl = 0; cl <= 9; cl++)
|
for (U32 cl = 0; cl <= 9; cl++)
|
||||||
{
|
{
|
||||||
|
|
@ -522,53 +484,78 @@ static bool sWriteStreamPNG(const String& bmType, Stream& stream, GBitmap* bitma
|
||||||
for (U32 pf = 0; pf < 6; pf++)
|
for (U32 pf = 0; pf < 6; pf++)
|
||||||
{
|
{
|
||||||
pMemStream->setPosition(0);
|
pMemStream->setPosition(0);
|
||||||
|
|
||||||
U32 waterMarkInner = FrameAllocator::getWaterMark();
|
U32 waterMarkInner = FrameAllocator::getWaterMark();
|
||||||
|
|
||||||
if (_writePNG(bitmap, *pMemStream, cl, zStrategies[zs], pngFilters[pf]) == false)
|
if (!_writePNG(bitmap, *pMemStream, cl, zStrategies[zs], pngFilters[pf]))
|
||||||
AssertFatal(false, "Handle this error!");
|
{
|
||||||
|
Con::errorf("sWriteStreamPNG - Compression search failed at cl=%d zs=%d pf=%d.", cl, zs, pf);
|
||||||
|
FrameAllocator::setWaterMark(waterMarkInner);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
FrameAllocator::setWaterMark(waterMarkInner);
|
FrameAllocator::setWaterMark(waterMarkInner);
|
||||||
|
|
||||||
if (pMemStream->getPosition() < minSize)
|
if (pMemStream->getPosition() < minSize)
|
||||||
{
|
{
|
||||||
minSize = pMemStream->getPosition();
|
minSize = pMemStream->getPosition();
|
||||||
bestStrategy = zs;
|
bestCLevel = cl; bestStrategy = zs; bestFilter = pf;
|
||||||
bestFilter = pf;
|
|
||||||
bestCLevel = cl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssertFatal(minSize != 0xFFFFFFFF, "Error, no best found?");
|
|
||||||
|
|
||||||
delete pMemStream;
|
delete pMemStream;
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
|
|
||||||
|
if (minSize == U32_MAX)
|
||||||
bool retVal = _writePNG(bitmap, stream,
|
{
|
||||||
bestCLevel,
|
Con::errorf("sWriteStreamPNG - No valid compression result found.");
|
||||||
zStrategies[bestStrategy],
|
|
||||||
pngFilters[bestFilter]);
|
|
||||||
FrameAllocator::setWaterMark(waterMark);
|
FrameAllocator::setWaterMark(waterMark);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool retVal = _writePNG(bitmap, stream, bestCLevel, zStrategies[bestStrategy], pngFilters[bestFilter]);
|
||||||
|
FrameAllocator::setWaterMark(waterMark);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
static bool sWritePNG(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel)
|
||||||
// Stores PNG stream data
|
|
||||||
struct DeferredPNGWriterData {
|
|
||||||
png_structp png_ptr;
|
|
||||||
png_infop info_ptr;
|
|
||||||
U32 width;
|
|
||||||
U32 height;
|
|
||||||
};
|
|
||||||
DeferredPNGWriter::DeferredPNGWriter() :
|
|
||||||
mData( NULL ),
|
|
||||||
mActive(false)
|
|
||||||
{
|
{
|
||||||
mData = new DeferredPNGWriterData();
|
FileStream writePng;
|
||||||
|
if (!writePng.open(path.getFullPath(), Torque::FS::File::Write))
|
||||||
|
{
|
||||||
|
Con::errorf("sWritePNG - Failed to open '%s' for writing.", path.getFullPath().c_str());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool retVal = sWriteStreamPNG("png", writePng, bitmap, compressionLevel);
|
||||||
|
if (!retVal)
|
||||||
|
Con::errorf("sWritePNG - Failed to encode '%s'. Format: %d, Size: %dx%d.",
|
||||||
|
path.getFullPath().c_str(), bitmap->getFormat(), bitmap->getWidth(), bitmap->getHeight());
|
||||||
|
|
||||||
|
writePng.close();
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sReadStreamPNG(Stream& stream, GBitmap* bitmap, U32 len)
|
||||||
|
{
|
||||||
|
return _readStreamPNG(stream, bitmap, len, "unknown (stream only)");
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// DeferredPNGWriter
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
struct DeferredPNGWriterData
|
||||||
|
{
|
||||||
|
PngWriteGuard guard;
|
||||||
|
U32 width = 0;
|
||||||
|
U32 height = 0;
|
||||||
|
|
||||||
|
explicit DeferredPNGWriterData(const char* filename) : guard(filename) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
DeferredPNGWriter::DeferredPNGWriter() : mData(nullptr), mActive(false) {}
|
||||||
|
|
||||||
DeferredPNGWriter::~DeferredPNGWriter()
|
DeferredPNGWriter::~DeferredPNGWriter()
|
||||||
{
|
{
|
||||||
delete mData;
|
delete mData;
|
||||||
|
|
@ -576,120 +563,52 @@ DeferredPNGWriter::~DeferredPNGWriter()
|
||||||
|
|
||||||
bool DeferredPNGWriter::begin(GFXFormat format, S32 width, S32 height, Stream& stream, U32 compressionLevel)
|
bool DeferredPNGWriter::begin(GFXFormat format, S32 width, S32 height, Stream& stream, U32 compressionLevel)
|
||||||
{
|
{
|
||||||
// ONLY RGB bitmap writing supported at this time!
|
if (!_isSupportedWriteFormat(format))
|
||||||
AssertFatal( format == GFXFormatR8G8B8 ||
|
|
||||||
format == GFXFormatR8G8B8A8 ||
|
|
||||||
format == GFXFormatR8G8B8X8 ||
|
|
||||||
format == GFXFormatA8 ||
|
|
||||||
format == GFXFormatR5G6B5, "_writePNG: ONLY RGB bitmap writing supported at this time.");
|
|
||||||
|
|
||||||
if ( format != GFXFormatR8G8B8 &&
|
|
||||||
format != GFXFormatR8G8B8A8 &&
|
|
||||||
format != GFXFormatR8G8B8X8 &&
|
|
||||||
format != GFXFormatA8 &&
|
|
||||||
format != GFXFormatR5G6B5 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
mData->png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
|
|
||||||
NULL,
|
|
||||||
pngFatalErrorFn,
|
|
||||||
pngWarningFn,
|
|
||||||
NULL,
|
|
||||||
pngRealMallocFn,
|
|
||||||
pngRealFreeFn);
|
|
||||||
if (mData->png_ptr == NULL)
|
|
||||||
return (false);
|
|
||||||
|
|
||||||
mData->info_ptr = png_create_info_struct(mData->png_ptr);
|
|
||||||
if (mData->info_ptr == NULL)
|
|
||||||
{
|
{
|
||||||
png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL);
|
Con::errorf("DeferredPNGWriter::begin - Unsupported format %d.", format);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
png_set_write_fn(mData->png_ptr, &stream, pngWriteDataFn, pngFlushDataFn);
|
mData = new DeferredPNGWriterData("DeferredPNGWriter");
|
||||||
|
|
||||||
// Set the compression level and image filters
|
if (!mData->guard.init(false))
|
||||||
png_set_compression_window_bits(mData->png_ptr, 15);
|
|
||||||
png_set_compression_level(mData->png_ptr, compressionLevel);
|
|
||||||
png_set_filter(mData->png_ptr, 0, PNG_ALL_FILTERS);
|
|
||||||
|
|
||||||
// Set the image information here. Width and height are up to 2^31,
|
|
||||||
// bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
|
|
||||||
// the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
|
|
||||||
// PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
|
|
||||||
// or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
|
|
||||||
// PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
|
|
||||||
// currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
|
|
||||||
|
|
||||||
if (format == GFXFormatR8G8B8)
|
|
||||||
{
|
{
|
||||||
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
Con::errorf("DeferredPNGWriter::begin - Failed to init PNG write structs. Format: %d, Size: %dx%d.", format, width, height);
|
||||||
width, height, // the width & height
|
return false;
|
||||||
8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type,
|
|
||||||
NULL, // no interlace
|
|
||||||
NULL, // compression type
|
|
||||||
NULL); // filter type
|
|
||||||
}
|
|
||||||
else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8)
|
|
||||||
{
|
|
||||||
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
|
||||||
width, height, // the width & height
|
|
||||||
8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type,
|
|
||||||
NULL, // no interlace
|
|
||||||
NULL, // compression type
|
|
||||||
NULL); // filter type
|
|
||||||
}
|
|
||||||
else if (format == GFXFormatA8)
|
|
||||||
{
|
|
||||||
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
|
||||||
width, height, // the width & height
|
|
||||||
8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
|
||||||
NULL, // no interlace
|
|
||||||
NULL, // compression type
|
|
||||||
NULL); // filter type
|
|
||||||
}
|
|
||||||
else if (format == GFXFormatR5G6B5)
|
|
||||||
{
|
|
||||||
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
|
||||||
width, height, // the width & height
|
|
||||||
16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
|
||||||
PNG_INTERLACE_NONE, // no interlace
|
|
||||||
PNG_COMPRESSION_TYPE_DEFAULT, // compression type
|
|
||||||
PNG_FILTER_TYPE_DEFAULT); // filter type
|
|
||||||
|
|
||||||
png_color_8_struct sigBit = { 0 };
|
|
||||||
sigBit.gray = 16;
|
|
||||||
png_set_sBIT(mData->png_ptr, mData->info_ptr, &sigBit );
|
|
||||||
|
|
||||||
png_set_swap( mData->png_ptr );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
png_write_info(mData->png_ptr, mData->info_ptr);
|
mData->width = width;
|
||||||
|
mData->height = height;
|
||||||
|
|
||||||
|
png_set_write_fn(mData->guard.png_ptr, &stream, pngWriteDataFn, pngFlushDataFn);
|
||||||
|
png_set_compression_window_bits(mData->guard.png_ptr, 15);
|
||||||
|
png_set_compression_level(mData->guard.png_ptr, compressionLevel);
|
||||||
|
png_set_filter(mData->guard.png_ptr, 0, PNG_ALL_FILTERS);
|
||||||
|
|
||||||
|
_applyWriteIHDR(mData->guard.png_ptr, mData->guard.info_ptr, format, width, height);
|
||||||
|
png_write_info(mData->guard.png_ptr, mData->guard.info_ptr);
|
||||||
|
|
||||||
mActive = true;
|
mActive = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeferredPNGWriter::append(GBitmap* bitmap, U32 rows)
|
void DeferredPNGWriter::append(GBitmap* bitmap, U32 rows)
|
||||||
{
|
{
|
||||||
AssertFatal(mActive, "Cannot append to an inactive DeferredPNGWriter!");
|
AssertFatal(mActive, "DeferredPNGWriter::append - Writer is not active.");
|
||||||
|
|
||||||
U32 height = getMin(bitmap->getHeight(), rows);
|
U32 height = getMin(bitmap->getHeight(), rows);
|
||||||
|
|
||||||
FrameAllocatorMarker marker;
|
FrameAllocatorMarker marker;
|
||||||
png_bytep* row_pointers = (png_bytep*)marker.alloc(height * sizeof(png_bytep));
|
png_bytep* row_pointers = (png_bytep*)marker.alloc(height * sizeof(png_bytep));
|
||||||
for (U32 i = 0; i < height; i++)
|
for (U32 i = 0; i < height; i++)
|
||||||
row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i));
|
row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i));
|
||||||
|
|
||||||
png_write_rows(mData->png_ptr, row_pointers, height);
|
png_write_rows(mData->guard.png_ptr, row_pointers, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeferredPNGWriter::end()
|
void DeferredPNGWriter::end()
|
||||||
{
|
{
|
||||||
AssertFatal(mActive, "Cannot end an inactive DeferredPNGWriter!");
|
AssertFatal(mActive, "DeferredPNGWriter::end - Writer is not active.");
|
||||||
|
|
||||||
png_write_end(mData->png_ptr, mData->info_ptr);
|
|
||||||
png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL);
|
|
||||||
|
|
||||||
|
png_write_end(mData->guard.png_ptr, mData->guard.info_ptr);
|
||||||
mActive = false;
|
mActive = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,24 @@
|
||||||
#include "gfx/gl/tGL/tXGL.h"
|
#include "gfx/gl/tGL/tXGL.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#pragma region GL WARNINGS
|
||||||
|
|
||||||
|
// #131204 - Texture state usage warning: The texture object (0) bound to texture image unit 0
|
||||||
|
#define GL_LOW_WARN_TEXTURE_STATE 131204
|
||||||
|
|
||||||
|
// #131169 - Framebuffer detailed info: The driver allocated storage for renderbuffer 2. (severity: low)
|
||||||
|
#define GL_LOW_WARN_FRAMEBUFFER 131169
|
||||||
|
|
||||||
|
// #131185 - Buffer detailed info: Buffer object 1 (bound to GL_ELEMENT_ARRAY_BUFFER_ARB, usage hint is GL_ENUM_88e4)
|
||||||
|
// will use VIDEO memory as the source for buffer object operations. (severity: low)
|
||||||
|
#define GL_LOW_WARN_VIDEO_MEMORY 131185
|
||||||
|
|
||||||
|
// #131218 - Program/shader state performance warning: Vertex shader in program #
|
||||||
|
// is being recompiled based on GL state. (severity: medium)
|
||||||
|
#define GL_MED_WARN_PERFORMANCE_RECOMPILE 131218
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
GFXAdapter::CreateDeviceInstanceDelegate GFXGLDevice::mCreateDeviceInstance(GFXGLDevice::createInstance);
|
GFXAdapter::CreateDeviceInstanceDelegate GFXGLDevice::mCreateDeviceInstance(GFXGLDevice::createInstance);
|
||||||
|
|
||||||
GFXDevice *GFXGLDevice::createInstance( U32 adapterIndex )
|
GFXDevice *GFXGLDevice::createInstance( U32 adapterIndex )
|
||||||
|
|
@ -104,6 +122,10 @@ void APIENTRY glDebugCallback(
|
||||||
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
|
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Silence: Texture state usage warning: The texture object (0) bound to texture image unit 0
|
||||||
|
if (id == GL_LOW_WARN_TEXTURE_STATE)
|
||||||
|
return;
|
||||||
|
|
||||||
const char* srcStr = "UNKNOWN";
|
const char* srcStr = "UNKNOWN";
|
||||||
const char* typeStr = "UNKNOWN";
|
const char* typeStr = "UNKNOWN";
|
||||||
const char* sevStr = "UNKNOWN";
|
const char* sevStr = "UNKNOWN";
|
||||||
|
|
|
||||||
|
|
@ -480,10 +480,9 @@ bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ImageUtil::isCompressedFormat(pDL->getFormat()))
|
if (mipLevels > 1 && !ImageUtil::isCompressedFormat(pDL->getFormat()))
|
||||||
glGenerateMipmap(texture->getBinding());
|
glGenerateMipmap(texture->getBinding());
|
||||||
|
|
||||||
glBindTexture(target, 0);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -560,10 +559,9 @@ bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numMips != 1 && !isCompressed)
|
if (numMips > 1 && !isCompressed)
|
||||||
glGenerateMipmap(texture->getBinding());
|
glGenerateMipmap(texture->getBinding());
|
||||||
|
|
||||||
glBindTexture(target, 0);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -608,7 +606,7 @@ bool GFXGLTextureManager::_refreshTexture(GFXTextureObject *texture)
|
||||||
_loadTexture(texture, texture->mBitmap);
|
_loadTexture(texture, texture->mBitmap);
|
||||||
|
|
||||||
if(texture->mDDS)
|
if(texture->mDDS)
|
||||||
return false;
|
_loadTexture(texture, texture->mDDS);
|
||||||
|
|
||||||
usedStrategies++;
|
usedStrategies++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,76 +105,33 @@ void GFXGLTextureObject::unlock(U32 mipLevel /*= 0*/, U32 faceIndex /*= 0*/)
|
||||||
if (!mLockedRect.bits)
|
if (!mLockedRect.bits)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PROFILE_SCOPE(GFXGLTextureObject_unlock);
|
// I know this is in unlock, but in GL we actually do our submission in unlock.
|
||||||
|
PROFILE_SCOPE(GFXGLTextureObject_lockRT);
|
||||||
|
|
||||||
PRESERVE_TEXTURE(mBinding);
|
PRESERVE_TEXTURE(mBinding);
|
||||||
glBindTexture(mBinding, mHandle);
|
glBindTexture(mBinding, mHandle);
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBuffer);
|
||||||
// --- Save pixel store state ---
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, (mLockedRectRect.extent.x + 1) * (mLockedRectRect.extent.y + 1) * mBytesPerTexel, mFrameAllocatorPtr, GL_STREAM_DRAW);
|
||||||
GLint prevUnpackAlign;
|
S32 z = getDepth();
|
||||||
glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlign);
|
|
||||||
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
|
|
||||||
const U32 width = mLockedRectRect.extent.x;
|
|
||||||
const U32 height = mLockedRectRect.extent.y;
|
|
||||||
const U32 depth = getDepth();
|
|
||||||
|
|
||||||
if (mBinding == GL_TEXTURE_3D)
|
if (mBinding == GL_TEXTURE_3D)
|
||||||
{
|
glTexSubImage3D(mBinding, mipLevel, mLockedRectRect.point.x, mLockedRectRect.point.y, z,
|
||||||
glTexSubImage3D(
|
mLockedRectRect.extent.x, mLockedRectRect.extent.y, z, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], NULL);
|
||||||
mBinding,
|
|
||||||
mipLevel,
|
|
||||||
mLockedRectRect.point.x,
|
|
||||||
mLockedRectRect.point.y,
|
|
||||||
0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
depth,
|
|
||||||
GFXGLTextureFormat[mFormat],
|
|
||||||
GFXGLTextureType[mFormat],
|
|
||||||
mLockedRect.bits
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (mBinding == GL_TEXTURE_2D)
|
else if (mBinding == GL_TEXTURE_2D)
|
||||||
{
|
glTexSubImage2D(mBinding, mipLevel, mLockedRectRect.point.x, mLockedRectRect.point.y,
|
||||||
glTexSubImage2D(
|
mLockedRectRect.extent.x, mLockedRectRect.extent.y, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], NULL);
|
||||||
mBinding,
|
|
||||||
mipLevel,
|
|
||||||
mLockedRectRect.point.x,
|
|
||||||
mLockedRectRect.point.y,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
GFXGLTextureFormat[mFormat],
|
|
||||||
GFXGLTextureType[mFormat],
|
|
||||||
mLockedRect.bits
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (mBinding == GL_TEXTURE_1D)
|
else if (mBinding == GL_TEXTURE_1D)
|
||||||
{
|
glTexSubImage1D(mBinding, mipLevel, (mLockedRectRect.point.x > 1 ? mLockedRectRect.point.x : mLockedRectRect.point.y),
|
||||||
glTexSubImage1D(
|
(mLockedRectRect.extent.x > 1 ? mLockedRectRect.extent.x : mLockedRectRect.extent.y), GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], NULL);
|
||||||
mBinding,
|
|
||||||
mipLevel,
|
|
||||||
mLockedRectRect.point.x,
|
|
||||||
width,
|
|
||||||
GFXGLTextureFormat[mFormat],
|
|
||||||
GFXGLTextureType[mFormat],
|
|
||||||
mLockedRect.bits
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Restore state ---
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlign);
|
|
||||||
|
|
||||||
mLockedRect.bits = NULL;
|
mLockedRect.bits = NULL;
|
||||||
|
#if TORQUE_DEBUG
|
||||||
|
AssertFatal(mFrameAllocatorMarkGuard == FrameAllocator::getWaterMark(), "");
|
||||||
|
#endif
|
||||||
FrameAllocator::setWaterMark(mFrameAllocatorMark);
|
FrameAllocator::setWaterMark(mFrameAllocatorMark);
|
||||||
mFrameAllocatorMark = 0;
|
mFrameAllocatorMark = 0;
|
||||||
mFrameAllocatorPtr = NULL;
|
mFrameAllocatorPtr = NULL;
|
||||||
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
glCheckErrors();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GFXGLTextureObject::release()
|
void GFXGLTextureObject::release()
|
||||||
|
|
@ -281,7 +238,6 @@ bool GFXGLTextureObject::copyToBmp(GBitmap * bmp)
|
||||||
} // face
|
} // face
|
||||||
} // mip
|
} // mip
|
||||||
|
|
||||||
glBindTexture(mBinding, 0);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,6 +254,9 @@ void GFXGLTextureObject::updateTextureSlot(const GFXTexHandle& texHandle, const
|
||||||
const GLenum srcTarget = srcTex->getBinding(); // source binding
|
const GLenum srcTarget = srcTex->getBinding(); // source binding
|
||||||
const bool srcIsCube = (srcTarget == GL_TEXTURE_CUBE_MAP || srcTarget == GL_TEXTURE_CUBE_MAP_ARRAY);
|
const bool srcIsCube = (srcTarget == GL_TEXTURE_CUBE_MAP || srcTarget == GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||||
|
|
||||||
|
PRESERVE_TEXTURE(srcTarget);
|
||||||
|
PRESERVE_TEXTURE(dstTarget);
|
||||||
|
|
||||||
// Determine list of faces to copy from source
|
// Determine list of faces to copy from source
|
||||||
U32 firstFace = 0;
|
U32 firstFace = 0;
|
||||||
U32 faceCount = 1;
|
U32 faceCount = 1;
|
||||||
|
|
@ -435,9 +394,6 @@ void GFXGLTextureObject::updateTextureSlot(const GFXTexHandle& texHandle, const
|
||||||
GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
|
GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glBindTexture(dstTarget, 0);
|
|
||||||
glBindTexture(srcTarget, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -462,6 +418,9 @@ void GFXGLTextureObject::initSamplerState(const GFXSamplerStateDesc &ssd)
|
||||||
|
|
||||||
void GFXGLTextureObject::bind(U32 textureUnit)
|
void GFXGLTextureObject::bind(U32 textureUnit)
|
||||||
{
|
{
|
||||||
|
if (!mHandle || mIsZombie)
|
||||||
|
return;
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
||||||
glBindTexture(mBinding, mHandle);
|
glBindTexture(mBinding, mHandle);
|
||||||
GFXGL->getOpenglCache()->setCacheBindedTex(textureUnit, mBinding, mHandle);
|
GFXGL->getOpenglCache()->setCacheBindedTex(textureUnit, mBinding, mHandle);
|
||||||
|
|
|
||||||
|
|
@ -1174,7 +1174,7 @@ GuiControl* GuiInspectorTypeColor::constructEditControl()
|
||||||
if( inspector->isMethod( "onInspectorPreFieldModification" ) )
|
if( inspector->isMethod( "onInspectorPreFieldModification" ) )
|
||||||
{
|
{
|
||||||
dSprintf( szBuffer, sizeof( szBuffer ),
|
dSprintf( szBuffer, sizeof( szBuffer ),
|
||||||
"%d.onInspectorPreFieldModification(\"%s\",\"%s\"); %s(%s, \"%d.onInspectorPostFieldModification(); %d.applyWithoutUndo\", %d.getRoot(), \"%d.applyWithoutUndo\", \"%d.onInspectorDiscardFieldModification(); %%unused=\");",
|
"%d.onInspectorPreFieldModification(\"%s\",\"%s\"); %s(%s, \"%d.onInspectorPostFieldModification(); %d.applyWithoutUndo\", %d.getRoot(), \"%d.applyWithoutUndo\", \"%d.onInspectorDiscardFieldModification(); %$unused=\");",
|
||||||
inspector->getId(), getRawFieldName(), getArrayIndex(),
|
inspector->getId(), getRawFieldName(), getArrayIndex(),
|
||||||
mColorFunction, szColor, inspector->getId(), getId(),
|
mColorFunction, szColor, inspector->getId(), getId(),
|
||||||
getId(),
|
getId(),
|
||||||
|
|
|
||||||
|
|
@ -434,25 +434,22 @@ void dampen(inout Surface surface, sampler2D WetnessTexture, float accumTime, fl
|
||||||
{
|
{
|
||||||
if (degree<=0.0) return;
|
if (degree<=0.0) return;
|
||||||
vec3 n = abs(surface.N);
|
vec3 n = abs(surface.N);
|
||||||
float ang = clamp(n.z, 0.04, 0.96);
|
float ang = 1.1-(abs(surface.N.z)*sign(surface.N.z));
|
||||||
|
|
||||||
float speed = -accumTime*(1.0-surface.linearRoughnessSq)*clamp((2.0-ang), 0.04, 0.96);
|
float speed = accumTime * (1.0 - surface.linearRoughnessSq);
|
||||||
if ((n.x > 0.0) || (n.y > 0.0))
|
vec3 wetoffset = (surface.P+vec3(speed,speed,speed)) * 0.33;
|
||||||
speed *= -1.0;
|
|
||||||
vec2 wetoffset = vec2(speed,speed)*0.1;
|
|
||||||
|
|
||||||
vec3 wetNormal = texture(WetnessTexture, float2(surface.P.xy*0.1+wetoffset)).xyz;
|
vec3 wetNormal = texture(WetnessTexture, wetoffset.xy).xyz * (1.1-(n.z* n.z));
|
||||||
wetNormal = lerp(wetNormal,texture(WetnessTexture,float2(surface.P.zx*0.1+wetoffset)).rgb ,n.y);
|
wetNormal = lerp(wetNormal, texture(WetnessTexture, wetoffset.zx).rgb, n.y);
|
||||||
wetNormal = lerp(wetNormal,texture(WetnessTexture,float2(surface.P.zy*0.1+wetoffset)).rgb ,n.x);
|
wetNormal = lerp(wetNormal, texture(WetnessTexture, wetoffset.zy).rgb, n.x);
|
||||||
surface.N = lerp(surface.N, wetNormal, degree);
|
wetNormal = normalize(wetNormal);
|
||||||
|
float wetness = wetNormal.b* degree;
|
||||||
|
|
||||||
float wetness = texture(WetnessTexture, vec2(surface.P.xy*0.1+wetoffset)).b;
|
wetNormal = normalize(wetNormal * 2.0 - 1.0);
|
||||||
wetness = lerp(wetness,texture(WetnessTexture,vec2(surface.P.zx*0.1+wetoffset)).b,n.y);
|
surface.N = normalize(vec3(surface.N.xy + wetNormal.xy * wetness, surface.N.z));
|
||||||
wetness = lerp(wetness,texture(WetnessTexture,vec2(surface.P.zy*0.1+wetoffset)).b,n.x);
|
|
||||||
wetness = pow(wetness*ang*degree,3);
|
|
||||||
|
|
||||||
surface.roughness = lerp(surface.roughness, 0.04f, wetness);
|
surface.roughness = lerp(surface.roughness, 0.04f, wetness);
|
||||||
surface.baseColor.rgb = lerp(surface.baseColor.rgb, surface.baseColor.rgb*0.6+float3(0.4,0.4,0.4)*wetness, wetness);
|
surface.baseColor = vec4(lerp(surface.baseColor.rgb, surface.baseColor.rgb*0.6+vec3(0.4,0.4,0.4)*wetness, wetness), max(surface.baseColor.a, 0.4* wetness));
|
||||||
surface.metalness = lerp(surface.metalness, 0.96, wetness);
|
surface.metalness = lerp(surface.metalness, 0.96, wetness);
|
||||||
updateSurface(surface);
|
updateSurface(surface);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -436,25 +436,22 @@ void dampen(inout Surface surface, TORQUE_SAMPLER2D(WetnessTexture), float accum
|
||||||
{
|
{
|
||||||
if (degree<=0.0) return;
|
if (degree<=0.0) return;
|
||||||
float3 n = abs(surface.N);
|
float3 n = abs(surface.N);
|
||||||
float ang = clamp(n.z, 0.04, 0.96);
|
float ang = 1.1-(abs(surface.N.z)*sign(surface.N.z));
|
||||||
|
|
||||||
float speed = -accumTime*(1.0-surface.linearRoughnessSq)*clamp((2.0-ang), 0.04, 0.96);
|
float speed = accumTime * (1.0 - surface.linearRoughnessSq);
|
||||||
if ((n.x > 0.0) || (n.y > 0.0))
|
float3 wetoffset = (surface.P+float3(speed,speed,speed)) * 0.33;
|
||||||
speed *= -1.0;
|
|
||||||
float2 wetoffset = float2(speed,speed)*0.1;
|
|
||||||
|
|
||||||
float3 wetNormal = TORQUE_TEX2D(WetnessTexture, float2(surface.P.xy*0.1+wetoffset)).xyz;
|
float3 wetNormal = TORQUE_TEX2D(WetnessTexture, wetoffset.xy).xyz * (1.1-(n.z* n.z));
|
||||||
wetNormal = lerp(wetNormal,TORQUE_TEX2D(WetnessTexture,float2(surface.P.zx*0.1+wetoffset)).rgb ,n.y);
|
wetNormal = lerp(wetNormal, TORQUE_TEX2D(WetnessTexture, wetoffset.zx).rgb, n.y);
|
||||||
wetNormal = lerp(wetNormal,TORQUE_TEX2D(WetnessTexture,float2(surface.P.zy*0.1+wetoffset)).rgb ,n.x);
|
wetNormal = lerp(wetNormal, TORQUE_TEX2D(WetnessTexture, wetoffset.zy).rgb, n.x);
|
||||||
surface.N = lerp(surface.N, wetNormal, degree);
|
wetNormal = normalize(wetNormal);
|
||||||
|
float wetness = wetNormal.b* degree;
|
||||||
|
|
||||||
float wetness = TORQUE_TEX2D(WetnessTexture, float2(surface.P.xy*0.1+wetoffset)).b;
|
wetNormal = normalize(wetNormal * 2.0 - 1.0);
|
||||||
wetness = lerp(wetness,TORQUE_TEX2D(WetnessTexture,float2(surface.P.zx*0.1+wetoffset)).b,n.y);
|
surface.N = normalize(float3(surface.N.xy + wetNormal.xy * wetness, surface.N.z));
|
||||||
wetness = lerp(wetness,TORQUE_TEX2D(WetnessTexture,float2(surface.P.zy*0.1+wetoffset)).b,n.x);
|
|
||||||
wetness = pow(wetness*ang*degree,3);
|
|
||||||
|
|
||||||
surface.roughness = lerp(surface.roughness, 0.04f, wetness);
|
surface.roughness = lerp(surface.roughness, 0.04f, wetness);
|
||||||
surface.baseColor.rgb = lerp(surface.baseColor.rgb, surface.baseColor.rgb*0.6+float3(0.4,0.4,0.4)*wetness, wetness);
|
surface.baseColor = float4(lerp(surface.baseColor.rgb, surface.baseColor.rgb * 0.6 + float3(0.4, 0.4, 0.4) * wetness, wetness), max(surface.baseColor.a, 0.4* wetness));
|
||||||
surface.metalness = lerp(surface.metalness, 0.96, wetness);
|
surface.metalness = lerp(surface.metalness, 0.96, wetness);
|
||||||
surface.Update();
|
surface.Update();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2101,7 +2101,7 @@ function EditorTree::onRightMouseUp( %this, %itemId, %mouse, %obj )
|
||||||
%popup.item[ 0 ] = "Delete" TAB "" TAB "EditorMenuEditDelete();";
|
%popup.item[ 0 ] = "Delete" TAB "" TAB "EditorMenuEditDelete();";
|
||||||
%popup.item[ 1 ] = "Group" TAB "" TAB "EWorldEditor.addSimGroup( true );";
|
%popup.item[ 1 ] = "Group" TAB "" TAB "EWorldEditor.addSimGroup( true );";
|
||||||
%popup.item[ 2 ] = "-";
|
%popup.item[ 2 ] = "-";
|
||||||
%popup.item[ 3 ] = "Make selected a Prefab" TAB "" TAB "EWorldEditor.makeSelectionPrefab();";
|
%popup.item[ 3 ] = "Make selected a Prefab" TAB "" TAB "EditorMakePrefab();";
|
||||||
%popup.item[ 4 ] = "Bake selected into Mesh" TAB "" TAB "AssetBrowser.setupCreateNewAsset(\"ShapeAsset\", AssetBrowser.selectedModule, \"makeSelectedAMesh\");";
|
%popup.item[ 4 ] = "Bake selected into Mesh" TAB "" TAB "AssetBrowser.setupCreateNewAsset(\"ShapeAsset\", AssetBrowser.selectedModule, \"makeSelectedAMesh\");";
|
||||||
%popup.item[ 5 ] = "-";
|
%popup.item[ 5 ] = "-";
|
||||||
%popup.item[ 6 ] = "Make selected a Sub-Level" TAB "" TAB "MakeSelectionASublevel();";
|
%popup.item[ 6 ] = "Make selected a Sub-Level" TAB "" TAB "MakeSelectionASublevel();";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue