Merge branch 'NDK-Refactor' into NDK
Conflicts: examples/HardwareInfo/main.cpp include/Nazara/Renderer/Enums.hpp include/Nazara/Renderer/GpuQuery.hpp include/Nazara/Renderer/OpenGL.hpp include/Nazara/Renderer/RenderBuffer.hpp include/Nazara/Renderer/RenderTexture.hpp include/Nazara/Renderer/Texture.hpp src/Nazara/Graphics/AbstractRenderTechnique.cpp src/Nazara/Graphics/DeferredRenderTechnique.cpp src/Nazara/Graphics/Material.cpp src/Nazara/Graphics/SkyboxBackground.cpp src/Nazara/Renderer/GpuQuery.cpp src/Nazara/Renderer/OpenGL.cpp src/Nazara/Renderer/RenderBuffer.cpp src/Nazara/Renderer/RenderTexture.cpp src/Nazara/Renderer/Renderer.cpp src/Nazara/Renderer/Shader.cpp src/Nazara/Renderer/ShaderStage.cpp src/Nazara/Renderer/Texture.cpp Former-commit-id: 2f1c7e9f9766f59ab83d9405856a1898ac4ab48f
This commit is contained in:
@@ -5,7 +5,10 @@
|
||||
#include <Nazara/Utility/AbstractAtlas.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzAbstractAtlas::~NzAbstractAtlas()
|
||||
namespace Nz
|
||||
{
|
||||
OnAtlasRelease(this);
|
||||
AbstractAtlas::~AbstractAtlas()
|
||||
{
|
||||
OnAtlasRelease(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,7 @@
|
||||
#include <Nazara/Utility/AbstractBuffer.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzAbstractBuffer::~NzAbstractBuffer() = default;
|
||||
namespace Nz
|
||||
{
|
||||
AbstractBuffer::~AbstractBuffer() = default;
|
||||
}
|
||||
|
||||
@@ -6,19 +6,22 @@
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzAbstractImage::~NzAbstractImage() = default;
|
||||
|
||||
nzUInt8 NzAbstractImage::GetBytesPerPixel() const
|
||||
namespace Nz
|
||||
{
|
||||
return NzPixelFormat::GetBytesPerPixel(GetFormat());
|
||||
}
|
||||
AbstractImage::~AbstractImage() = default;
|
||||
|
||||
bool NzAbstractImage::IsCompressed() const
|
||||
{
|
||||
return NzPixelFormat::IsCompressed(GetFormat());
|
||||
}
|
||||
UInt8 AbstractImage::GetBytesPerPixel() const
|
||||
{
|
||||
return PixelFormat::GetBytesPerPixel(GetFormat());
|
||||
}
|
||||
|
||||
bool NzAbstractImage::IsCubemap() const
|
||||
{
|
||||
return GetType() == nzImageType_Cubemap;
|
||||
bool AbstractImage::IsCompressed() const
|
||||
{
|
||||
return PixelFormat::IsCompressed(GetFormat());
|
||||
}
|
||||
|
||||
bool AbstractImage::IsCubemap() const
|
||||
{
|
||||
return GetType() == ImageType_Cubemap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,8 @@
|
||||
#include <Nazara/Utility/AbstractTextDrawer.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzAbstractTextDrawer::~NzAbstractTextDrawer() = default;
|
||||
namespace Nz
|
||||
{
|
||||
AbstractTextDrawer::~AbstractTextDrawer() = default;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -15,287 +15,290 @@
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
NzAbstractBuffer* SoftwareBufferFactory(NzBuffer* parent, nzBufferType type)
|
||||
namespace
|
||||
{
|
||||
return new NzSoftwareBuffer(parent, type);
|
||||
}
|
||||
}
|
||||
|
||||
NzBuffer::NzBuffer(nzBufferType type) :
|
||||
m_type(type),
|
||||
m_impl(nullptr),
|
||||
m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
NzBuffer::NzBuffer(nzBufferType type, unsigned int size, nzUInt32 storage, nzBufferUsage usage) :
|
||||
m_type(type),
|
||||
m_impl(nullptr)
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
|
||||
Create(size, storage, usage);
|
||||
}
|
||||
|
||||
NzBuffer::~NzBuffer()
|
||||
{
|
||||
OnBufferRelease(this);
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool NzBuffer::CopyContent(const NzBuffer& buffer)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer must be valid");
|
||||
return false;
|
||||
AbstractBuffer* SoftwareBufferFactory(Buffer* parent, BufferType type)
|
||||
{
|
||||
return new SoftwareBuffer(parent, type);
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer.IsValid())
|
||||
Buffer::Buffer(BufferType type) :
|
||||
m_type(type),
|
||||
m_impl(nullptr),
|
||||
m_size(0)
|
||||
{
|
||||
NazaraError("Source buffer must be valid");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
NzBufferMapper<NzBuffer> mapper(buffer, nzBufferAccess_ReadOnly);
|
||||
return Fill(mapper.GetPointer(), 0, buffer.GetSize());
|
||||
}
|
||||
|
||||
bool NzBuffer::Create(unsigned int size, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
// Notre buffer est-il supporté ?
|
||||
if (!IsStorageSupported(storage))
|
||||
{
|
||||
NazaraError("Buffer storage not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<NzAbstractBuffer> impl(s_bufferFactories[storage](this, m_type));
|
||||
if (!impl->Create(size, usage))
|
||||
Buffer::Buffer(BufferType type, unsigned int size, UInt32 storage, BufferUsage usage) :
|
||||
m_type(type),
|
||||
m_impl(nullptr)
|
||||
{
|
||||
NazaraError("Failed to create buffer");
|
||||
return false;
|
||||
ErrorFlags flags(ErrorFlag_ThrowException, true);
|
||||
Create(size, storage, usage);
|
||||
}
|
||||
|
||||
m_impl = impl.release();
|
||||
m_size = size;
|
||||
m_storage = storage;
|
||||
m_usage = usage;
|
||||
|
||||
return true; // Si on arrive ici c'est que tout s'est bien passé.
|
||||
}
|
||||
|
||||
void NzBuffer::Destroy()
|
||||
{
|
||||
if (m_impl)
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
OnBufferDestroy(this);
|
||||
OnBufferRelease(this);
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool Buffer::CopyContent(const Buffer& buffer)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer must be valid");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!buffer.IsValid())
|
||||
{
|
||||
NazaraError("Source buffer must be valid");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
BufferMapper<Buffer> mapper(buffer, BufferAccess_ReadOnly);
|
||||
return Fill(mapper.GetPointer(), 0, buffer.GetSize());
|
||||
}
|
||||
|
||||
bool Buffer::Create(unsigned int size, UInt32 storage, BufferUsage usage)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
// Notre buffer est-il supporté ?
|
||||
if (!IsStorageSupported(storage))
|
||||
{
|
||||
NazaraError("Buffer storage not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractBuffer> impl(s_bufferFactories[storage](this, m_type));
|
||||
if (!impl->Create(size, usage))
|
||||
{
|
||||
NazaraError("Failed to create buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_impl = impl.release();
|
||||
m_size = size;
|
||||
m_storage = storage;
|
||||
m_usage = usage;
|
||||
|
||||
return true; // Si on arrive ici c'est que tout s'est bien passé.
|
||||
}
|
||||
|
||||
void Buffer::Destroy()
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
OnBufferDestroy(this);
|
||||
|
||||
m_impl->Destroy();
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Buffer::Fill(const void* data, unsigned int offset, unsigned int size, bool forceDiscard)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (offset+size > m_size)
|
||||
{
|
||||
NazaraError("Exceeding buffer size (" + String::Number(offset+size) + " > " + String::Number(m_size) + ')');
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_impl->Fill(data, offset, (size == 0) ? m_size-offset : size, forceDiscard);
|
||||
}
|
||||
|
||||
AbstractBuffer* Buffer::GetImpl() const
|
||||
{
|
||||
return m_impl;
|
||||
}
|
||||
|
||||
unsigned int Buffer::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
UInt32 Buffer::GetStorage() const
|
||||
{
|
||||
return m_storage;
|
||||
}
|
||||
|
||||
BufferType Buffer::GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
BufferUsage Buffer::GetUsage() const
|
||||
{
|
||||
return m_usage;
|
||||
}
|
||||
|
||||
bool Buffer::IsHardware() const
|
||||
{
|
||||
return m_storage & DataStorage_Hardware;
|
||||
}
|
||||
|
||||
bool Buffer::IsValid() const
|
||||
{
|
||||
return m_impl != nullptr;
|
||||
}
|
||||
|
||||
void* Buffer::Map(BufferAccess access, unsigned int offset, unsigned int size)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (offset+size > m_size)
|
||||
{
|
||||
NazaraError("Exceeding buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_impl->Map(access, offset, (size == 0) ? m_size-offset : size);
|
||||
}
|
||||
|
||||
void* Buffer::Map(BufferAccess access, unsigned int offset, unsigned int size) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (access != BufferAccess_ReadOnly)
|
||||
{
|
||||
NazaraError("Buffer access must be read-only when used const");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (offset+size > m_size)
|
||||
{
|
||||
NazaraError("Exceeding buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_impl->Map(access, offset, (size == 0) ? m_size-offset : size);
|
||||
}
|
||||
|
||||
bool Buffer::SetStorage(UInt32 storage)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_storage == storage)
|
||||
return true;
|
||||
|
||||
if (!IsStorageSupported(storage))
|
||||
{
|
||||
NazaraError("Storage not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
void* ptr = m_impl->Map(BufferAccess_ReadOnly, 0, m_size);
|
||||
if (!ptr)
|
||||
{
|
||||
NazaraError("Failed to map buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
CallOnExit unmapMyImpl([this]()
|
||||
{
|
||||
m_impl->Unmap();
|
||||
});
|
||||
|
||||
std::unique_ptr<AbstractBuffer> impl(s_bufferFactories[storage](this, m_type));
|
||||
if (!impl->Create(m_size, m_usage))
|
||||
{
|
||||
NazaraError("Failed to create buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
CallOnExit destroyImpl([&impl]()
|
||||
{
|
||||
impl->Destroy();
|
||||
});
|
||||
|
||||
if (!impl->Fill(ptr, 0, m_size))
|
||||
{
|
||||
NazaraError("Failed to fill buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
destroyImpl.Reset();
|
||||
|
||||
unmapMyImpl.CallAndReset();
|
||||
m_impl->Destroy();
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool NzBuffer::Fill(const void* data, unsigned int offset, unsigned int size, bool forceDiscard)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return false;
|
||||
}
|
||||
m_impl = impl.release();
|
||||
m_storage = storage;
|
||||
|
||||
if (offset+size > m_size)
|
||||
{
|
||||
NazaraError("Exceeding buffer size (" + NzString::Number(offset+size) + " > " + NzString::Number(m_size) + ')');
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_impl->Fill(data, offset, (size == 0) ? m_size-offset : size, forceDiscard);
|
||||
}
|
||||
|
||||
NzAbstractBuffer* NzBuffer::GetImpl() const
|
||||
{
|
||||
return m_impl;
|
||||
}
|
||||
|
||||
unsigned int NzBuffer::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
nzUInt32 NzBuffer::GetStorage() const
|
||||
{
|
||||
return m_storage;
|
||||
}
|
||||
|
||||
nzBufferType NzBuffer::GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
nzBufferUsage NzBuffer::GetUsage() const
|
||||
{
|
||||
return m_usage;
|
||||
}
|
||||
|
||||
bool NzBuffer::IsHardware() const
|
||||
{
|
||||
return m_storage & nzDataStorage_Hardware;
|
||||
}
|
||||
|
||||
bool NzBuffer::IsValid() const
|
||||
{
|
||||
return m_impl != nullptr;
|
||||
}
|
||||
|
||||
void* NzBuffer::Map(nzBufferAccess access, unsigned int offset, unsigned int size)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (offset+size > m_size)
|
||||
{
|
||||
NazaraError("Exceeding buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_impl->Map(access, offset, (size == 0) ? m_size-offset : size);
|
||||
}
|
||||
|
||||
void* NzBuffer::Map(nzBufferAccess access, unsigned int offset, unsigned int size) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (access != nzBufferAccess_ReadOnly)
|
||||
{
|
||||
NazaraError("Buffer access must be read-only when used const");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (offset+size > m_size)
|
||||
{
|
||||
NazaraError("Exceeding buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_impl->Map(access, offset, (size == 0) ? m_size-offset : size);
|
||||
}
|
||||
|
||||
bool NzBuffer::SetStorage(nzUInt32 storage)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_storage == storage)
|
||||
return true;
|
||||
|
||||
if (!IsStorageSupported(storage))
|
||||
{
|
||||
NazaraError("Storage not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
void* ptr = m_impl->Map(nzBufferAccess_ReadOnly, 0, m_size);
|
||||
if (!ptr)
|
||||
void Buffer::Unmap() const
|
||||
{
|
||||
NazaraError("Failed to map buffer");
|
||||
return false;
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_impl->Unmap())
|
||||
NazaraWarning("Failed to unmap buffer (it's content may be undefined)"); ///TODO: Unexpected ?
|
||||
}
|
||||
|
||||
NzCallOnExit unmapMyImpl([this]()
|
||||
bool Buffer::IsStorageSupported(UInt32 storage)
|
||||
{
|
||||
m_impl->Unmap();
|
||||
});
|
||||
|
||||
std::unique_ptr<NzAbstractBuffer> impl(s_bufferFactories[storage](this, m_type));
|
||||
if (!impl->Create(m_size, m_usage))
|
||||
{
|
||||
NazaraError("Failed to create buffer");
|
||||
return false;
|
||||
return s_bufferFactories[storage] != nullptr;
|
||||
}
|
||||
|
||||
NzCallOnExit destroyImpl([&impl]()
|
||||
void Buffer::SetBufferFactory(UInt32 storage, BufferFactory func)
|
||||
{
|
||||
impl->Destroy();
|
||||
});
|
||||
|
||||
if (!impl->Fill(ptr, 0, m_size))
|
||||
{
|
||||
NazaraError("Failed to fill buffer");
|
||||
return false;
|
||||
s_bufferFactories[storage] = func;
|
||||
}
|
||||
|
||||
destroyImpl.Reset();
|
||||
|
||||
unmapMyImpl.CallAndReset();
|
||||
m_impl->Destroy();
|
||||
delete m_impl;
|
||||
|
||||
m_impl = impl.release();
|
||||
m_storage = storage;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzBuffer::Unmap() const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
bool Buffer::Initialize()
|
||||
{
|
||||
NazaraError("Buffer not valid");
|
||||
return;
|
||||
s_bufferFactories[DataStorage_Software] = SoftwareBufferFactory;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_impl->Unmap())
|
||||
NazaraWarning("Failed to unmap buffer (it's content may be undefined)"); ///TODO: Unexpected ?
|
||||
void Buffer::Uninitialize()
|
||||
{
|
||||
std::memset(s_bufferFactories, 0, (DataStorage_Max+1)*sizeof(Buffer::BufferFactory));
|
||||
}
|
||||
|
||||
Buffer::BufferFactory Buffer::s_bufferFactories[DataStorage_Max+1] = {nullptr};
|
||||
}
|
||||
|
||||
bool NzBuffer::IsStorageSupported(nzUInt32 storage)
|
||||
{
|
||||
return s_bufferFactories[storage] != nullptr;
|
||||
}
|
||||
|
||||
void NzBuffer::SetBufferFactory(nzUInt32 storage, BufferFactory func)
|
||||
{
|
||||
s_bufferFactories[storage] = func;
|
||||
}
|
||||
|
||||
bool NzBuffer::Initialize()
|
||||
{
|
||||
s_bufferFactories[nzDataStorage_Software] = SoftwareBufferFactory;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzBuffer::Uninitialize()
|
||||
{
|
||||
std::memset(s_bufferFactories, 0, (nzDataStorage_Max+1)*sizeof(NzBuffer::BufferFactory));
|
||||
}
|
||||
|
||||
NzBuffer::BufferFactory NzBuffer::s_bufferFactories[nzDataStorage_Max+1] = {nullptr};
|
||||
|
||||
@@ -14,50 +14,53 @@
|
||||
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzCursor::NzCursor() :
|
||||
m_impl(nullptr)
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
NzCursor::~NzCursor()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool NzCursor::Create(const NzImage& cursor, int hotSpotX, int hotSpotY)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
m_impl = new NzCursorImpl;
|
||||
if (!m_impl->Create(cursor, hotSpotX, hotSpotY))
|
||||
Cursor::Cursor() :
|
||||
m_impl(nullptr)
|
||||
{
|
||||
NazaraError("Failed to create cursor implementation");
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzCursor::Create(const NzImage& cursor, const NzVector2i& hotSpot)
|
||||
{
|
||||
return Create(cursor, hotSpot.x, hotSpot.y);
|
||||
}
|
||||
|
||||
void NzCursor::Destroy()
|
||||
{
|
||||
if (m_impl)
|
||||
Cursor::~Cursor()
|
||||
{
|
||||
m_impl->Destroy();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
bool Cursor::Create(const Image& cursor, int hotSpotX, int hotSpotY)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
m_impl = new CursorImpl;
|
||||
if (!m_impl->Create(cursor, hotSpotX, hotSpotY))
|
||||
{
|
||||
NazaraError("Failed to create cursor implementation");
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cursor::Create(const Image& cursor, const Vector2i& hotSpot)
|
||||
{
|
||||
return Create(cursor, hotSpot.x, hotSpot.y);
|
||||
}
|
||||
|
||||
void Cursor::Destroy()
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
m_impl->Destroy();
|
||||
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cursor::IsValid() const
|
||||
{
|
||||
return m_impl != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool NzCursor::IsValid() const
|
||||
{
|
||||
return m_impl != nullptr;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,4 +5,7 @@
|
||||
#include <Nazara/Utility/FontData.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzFontData::~NzFontData() = default;
|
||||
namespace Nz
|
||||
{
|
||||
FontData::~FontData() = default;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
#ifndef NAZARA_LOADERS_DDS_CONSTANTS_HPP
|
||||
#define NAZARA_LOADERS_DDS_CONSTANTS_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
#define DDS_MAGIC 0x20534444
|
||||
#define DDS_DXT1 0x31545844
|
||||
#define DDS_DXT3 0x33545844
|
||||
#define DDS_DXT5 0x35545844
|
||||
|
||||
inline constexpr nzUInt32 FourCC(nzUInt32 a, nzUInt32 b, nzUInt32 c, nzUInt32 d)
|
||||
inline constexpr Nz::UInt32 FourCC(Nz::UInt32 a, Nz::UInt32 b, Nz::UInt32 c, Nz::UInt32 d)
|
||||
{
|
||||
return a << 0 |
|
||||
b << 8 |
|
||||
@@ -20,7 +22,7 @@ inline constexpr nzUInt32 FourCC(nzUInt32 a, nzUInt32 b, nzUInt32 c, nzUInt32 d)
|
||||
d << 24;
|
||||
}
|
||||
|
||||
enum D3D10_RESOURCE_DIMENSION : nzUInt32
|
||||
enum D3D10_RESOURCE_DIMENSION : Nz::UInt32
|
||||
{
|
||||
D3D10_RESOURCE_DIMENSION_UNKNOWN = 0,
|
||||
D3D10_RESOURCE_DIMENSION_BUFFER = 1,
|
||||
@@ -38,7 +40,7 @@ enum D3D10_RESOURCE_MISC
|
||||
D3D10_RESOURCE_MISC_GDI_COMPATIBLE = 0x20L
|
||||
};
|
||||
|
||||
enum D3DFMT : nzUInt32
|
||||
enum D3DFMT : Nz::UInt32
|
||||
{
|
||||
D3DFMT_UNKNOWN = 0,
|
||||
|
||||
@@ -222,7 +224,7 @@ enum DDS_SAVE
|
||||
DDS_SAVE_MAX
|
||||
};
|
||||
|
||||
enum DXGI_FORMAT : nzUInt32
|
||||
enum DXGI_FORMAT : Nz::UInt32
|
||||
{
|
||||
DXGI_FORMAT_UNKNOWN = 0,
|
||||
DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
|
||||
@@ -328,47 +330,47 @@ enum DXGI_FORMAT : nzUInt32
|
||||
|
||||
struct DDSPixelFormat // DDPIXELFORMAT
|
||||
{
|
||||
nzUInt32 size;
|
||||
nzUInt32 flags;
|
||||
nzUInt32 fourCC;
|
||||
nzUInt32 bpp;
|
||||
nzUInt32 redMask;
|
||||
nzUInt32 greenMask;
|
||||
nzUInt32 blueMask;
|
||||
nzUInt32 alphaMask;
|
||||
Nz::UInt32 size;
|
||||
Nz::UInt32 flags;
|
||||
Nz::UInt32 fourCC;
|
||||
Nz::UInt32 bpp;
|
||||
Nz::UInt32 redMask;
|
||||
Nz::UInt32 greenMask;
|
||||
Nz::UInt32 blueMask;
|
||||
Nz::UInt32 alphaMask;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DDSPixelFormat) == 8*sizeof(nzUInt32), "DDSPixelFormat must be packed");
|
||||
static_assert(sizeof(DDSPixelFormat) == 8*sizeof(Nz::UInt32), "DDSPixelFormat must be packed");
|
||||
|
||||
struct DDSHeader
|
||||
{
|
||||
nzUInt32 size;
|
||||
nzUInt32 flags;
|
||||
nzUInt32 height;
|
||||
nzUInt32 width;
|
||||
nzUInt32 pitch;
|
||||
nzUInt32 depth;
|
||||
nzUInt32 levelCount;
|
||||
nzUInt32 reserved1[11];
|
||||
Nz::UInt32 size;
|
||||
Nz::UInt32 flags;
|
||||
Nz::UInt32 height;
|
||||
Nz::UInt32 width;
|
||||
Nz::UInt32 pitch;
|
||||
Nz::UInt32 depth;
|
||||
Nz::UInt32 levelCount;
|
||||
Nz::UInt32 reserved1[11];
|
||||
DDSPixelFormat format;
|
||||
nzUInt32 ddsCaps1;
|
||||
nzUInt32 ddsCaps2;
|
||||
nzUInt32 ddsCaps3;
|
||||
nzUInt32 ddsCaps4;
|
||||
nzUInt32 reserved2;
|
||||
Nz::UInt32 ddsCaps1;
|
||||
Nz::UInt32 ddsCaps2;
|
||||
Nz::UInt32 ddsCaps3;
|
||||
Nz::UInt32 ddsCaps4;
|
||||
Nz::UInt32 reserved2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DDSHeader) == 23*sizeof(nzUInt32) + sizeof(DDSPixelFormat), "DDSHeader must be packed");
|
||||
static_assert(sizeof(DDSHeader) == 23*sizeof(Nz::UInt32) + sizeof(DDSPixelFormat), "DDSHeader must be packed");
|
||||
|
||||
struct DDSHeaderDX10Ext
|
||||
{
|
||||
DXGI_FORMAT dxgiFormat;
|
||||
D3D10_RESOURCE_DIMENSION resourceDimension;
|
||||
nzUInt32 miscFlag;
|
||||
nzUInt32 arraySize;
|
||||
nzUInt32 reserved;
|
||||
Nz::UInt32 miscFlag;
|
||||
Nz::UInt32 arraySize;
|
||||
Nz::UInt32 reserved;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DDSHeaderDX10Ext) == 5*sizeof(nzUInt32), "DDSHeaderDX10Ext must be packed");
|
||||
static_assert(sizeof(DDSHeaderDX10Ext) == 5*sizeof(Nz::UInt32), "DDSHeaderDX10Ext must be packed");
|
||||
|
||||
#endif // NAZARA_LOADERS_DDS_CONSTANTS_HPP
|
||||
|
||||
@@ -11,113 +11,119 @@
|
||||
#include <memory>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
bool IsSupported(const NzString& extension)
|
||||
namespace
|
||||
{
|
||||
return (extension == "dds");
|
||||
}
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzImageParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
nzUInt32 magic;
|
||||
if (stream.Read(&magic, sizeof(nzUInt32)) == sizeof(nzUInt32))
|
||||
bool IsSupported(const String& extension)
|
||||
{
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(&magic, sizeof(nzUInt32));
|
||||
#endif
|
||||
|
||||
if (magic == DDS_MAGIC)
|
||||
return nzTernary_True;
|
||||
return (extension == "dds");
|
||||
}
|
||||
|
||||
return nzTernary_False;
|
||||
}
|
||||
|
||||
bool Load(NzImage* image, NzInputStream& stream, const NzImageParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
DDSHeader header;
|
||||
if (stream.Read(&header, sizeof(DDSHeader)) != sizeof(DDSHeader))
|
||||
Ternary Check(InputStream& stream, const ImageParams& parameters)
|
||||
{
|
||||
NazaraError("Failed to read DDS header");
|
||||
return false;
|
||||
}
|
||||
NazaraUnused(parameters);
|
||||
|
||||
DDSHeaderDX10Ext headerDX10;
|
||||
if (header.format.flags & DDPF_FOURCC && header.format.fourCC == D3DFMT_DX10)
|
||||
{
|
||||
if (stream.Read(&headerDX10, sizeof(DDSHeaderDX10Ext)) != sizeof(DDSHeaderDX10Ext))
|
||||
UInt32 magic;
|
||||
if (stream.Read(&magic, sizeof(UInt32)) == sizeof(UInt32))
|
||||
{
|
||||
NazaraError("Failed to read DDS DX10 extension header");
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
ByteSwap(&magic, sizeof(UInt32));
|
||||
#endif
|
||||
|
||||
if (magic == DDS_MAGIC)
|
||||
return Ternary_True;
|
||||
}
|
||||
|
||||
return Ternary_False;
|
||||
}
|
||||
|
||||
bool Load(Image* image, InputStream& stream, const ImageParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
DDSHeader header;
|
||||
if (stream.Read(&header, sizeof(DDSHeader)) != sizeof(DDSHeader))
|
||||
{
|
||||
NazaraError("Failed to read DDS header");
|
||||
return false;
|
||||
}
|
||||
|
||||
DDSHeaderDX10Ext headerDX10;
|
||||
if (header.format.flags & DDPF_FOURCC && header.format.fourCC == D3DFMT_DX10)
|
||||
{
|
||||
if (stream.Read(&headerDX10, sizeof(DDSHeaderDX10Ext)) != sizeof(DDSHeaderDX10Ext))
|
||||
{
|
||||
NazaraError("Failed to read DDS DX10 extension header");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
headerDX10.arraySize = 1;
|
||||
headerDX10.dxgiFormat = DXGI_FORMAT_UNKNOWN;
|
||||
headerDX10.miscFlag = 0;
|
||||
headerDX10.resourceDimension = D3D10_RESOURCE_DIMENSION_UNKNOWN;
|
||||
}
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
// Les fichiers DDS sont en little endian
|
||||
ByteSwap(&header.size, sizeof(UInt32));
|
||||
ByteSwap(&header.flags, sizeof(UInt32));
|
||||
ByteSwap(&header.height, sizeof(UInt32));
|
||||
ByteSwap(&header.width, sizeof(UInt32));
|
||||
ByteSwap(&header.pitch, sizeof(UInt32));
|
||||
ByteSwap(&header.depth, sizeof(UInt32));
|
||||
ByteSwap(&header.levelCount, sizeof(UInt32));
|
||||
|
||||
// DDS_PixelFormat
|
||||
ByteSwap(&header.format.size, sizeof(UInt32));
|
||||
ByteSwap(&header.format.flags, sizeof(UInt32));
|
||||
ByteSwap(&header.format.fourCC, sizeof(UInt32));
|
||||
ByteSwap(&header.format.bpp, sizeof(UInt32));
|
||||
ByteSwap(&header.format.redMask, sizeof(UInt32));
|
||||
ByteSwap(&header.format.greenMask, sizeof(UInt32));
|
||||
ByteSwap(&header.format.blueMask, sizeof(UInt32));
|
||||
ByteSwap(&header.format.alphaMask, sizeof(UInt32));
|
||||
|
||||
ByteSwap(&header.ddsCaps1, sizeof(UInt32));
|
||||
ByteSwap(&header.ddsCaps2, sizeof(UInt32));
|
||||
ByteSwap(&header.ddsCaps3, sizeof(UInt32));
|
||||
ByteSwap(&header.ddsCaps4, sizeof(UInt32));
|
||||
#endif
|
||||
|
||||
unsigned int width = header.width;
|
||||
unsigned int height = header.height;
|
||||
unsigned int depth = std::max(header.depth, 1U);
|
||||
unsigned int levelCount = (parameters.levelCount > 0) ? std::min(parameters.levelCount, static_cast<UInt8>(header.levelCount)) : header.levelCount;
|
||||
|
||||
// Détermination du type
|
||||
ImageType type;
|
||||
if (header.ddsCaps2 & DDSCAPS2_CUBEMAP)
|
||||
type = ImageType_Cubemap;
|
||||
else if (header.ddsCaps2 & DDSCAPS2_VOLUME)
|
||||
type = ImageType_3D;
|
||||
|
||||
// Détermination du format
|
||||
PixelFormatType format;
|
||||
|
||||
if (parameters.loadFormat != PixelFormatType_Undefined)
|
||||
image->Convert(parameters.loadFormat);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterDDS()
|
||||
{
|
||||
headerDX10.arraySize = 1;
|
||||
headerDX10.dxgiFormat = DXGI_FORMAT_UNKNOWN;
|
||||
headerDX10.miscFlag = 0;
|
||||
headerDX10.resourceDimension = D3D10_RESOURCE_DIMENSION_UNKNOWN;
|
||||
ImageLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
// Les fichiers DDS sont en little endian
|
||||
NzByteSwap(&header.size, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.flags, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.height, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.width, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.pitch, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.depth, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.levelCount, sizeof(nzUInt32));
|
||||
|
||||
// DDS_PixelFormat
|
||||
NzByteSwap(&header.format.size, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.format.flags, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.format.fourCC, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.format.bpp, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.format.redMask, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.format.greenMask, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.format.blueMask, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.format.alphaMask, sizeof(nzUInt32));
|
||||
|
||||
NzByteSwap(&header.ddsCaps1, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.ddsCaps2, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.ddsCaps3, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.ddsCaps4, sizeof(nzUInt32));
|
||||
#endif
|
||||
|
||||
unsigned int width = header.width;
|
||||
unsigned int height = header.height;
|
||||
unsigned int depth = std::max(header.depth, 1U);
|
||||
unsigned int levelCount = (parameters.levelCount > 0) ? std::min(parameters.levelCount, static_cast<nzUInt8>(header.levelCount)) : header.levelCount;
|
||||
|
||||
// Détermination du type
|
||||
nzImageType type;
|
||||
if (header.ddsCaps2 & DDSCAPS2_CUBEMAP)
|
||||
type = nzImageType_Cubemap;
|
||||
else if (header.ddsCaps2 & DDSCAPS2_VOLUME)
|
||||
type = nzImageType_3D;
|
||||
|
||||
// Détermination du format
|
||||
nzPixelFormat format;
|
||||
|
||||
if (parameters.loadFormat != nzPixelFormat_Undefined)
|
||||
image->Convert(parameters.loadFormat);
|
||||
|
||||
return true;
|
||||
void UnregisterDDS()
|
||||
{
|
||||
ImageLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_DDS_Register()
|
||||
{
|
||||
NzImageLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_DDS_Unregister()
|
||||
{
|
||||
NzImageLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_DDS_Register();
|
||||
void NzLoaders_DDS_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterDDS();
|
||||
void UnregisterDDS();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_DDS_HPP
|
||||
|
||||
@@ -17,437 +17,443 @@
|
||||
#include <set>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
class FreeTypeLibrary;
|
||||
|
||||
FT_Library s_library;
|
||||
std::shared_ptr<FreeTypeLibrary> s_libraryOwner;
|
||||
float s_invScaleFactor = 1.f / (1 << 6); // 1/64
|
||||
|
||||
extern "C"
|
||||
unsigned long FT_StreamRead(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count)
|
||||
namespace
|
||||
{
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-system_interface.html#FT_Stream_IoFunc
|
||||
NzInputStream& inputStream = *static_cast<NzInputStream*>(stream->descriptor.pointer);
|
||||
class FreeTypeLibrary;
|
||||
|
||||
// La valeur de count indique une opération de lecture ou de positionnement
|
||||
if (count > 0)
|
||||
FT_Library s_library;
|
||||
std::shared_ptr<FreeTypeLibrary> s_libraryOwner;
|
||||
float s_invScaleFactor = 1.f / (1 << 6); // 1/64
|
||||
|
||||
extern "C"
|
||||
unsigned long FT_StreamRead(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count)
|
||||
{
|
||||
// Dans le premier cas, une erreur est symbolisée par un retour nul
|
||||
if (inputStream.SetCursorPos(offset))
|
||||
return static_cast<unsigned long>(inputStream.Read(buffer, count));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dans le second cas, une erreur est symbolisée par un retour non-nul
|
||||
if (inputStream.SetCursorPos(offset))
|
||||
return 0;
|
||||
else
|
||||
return 42; // La réponse à la grande question
|
||||
}
|
||||
}
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-system_interface.html#FT_Stream_IoFunc
|
||||
InputStream& inputStream = *static_cast<InputStream*>(stream->descriptor.pointer);
|
||||
|
||||
extern "C"
|
||||
void FT_StreamClose(FT_Stream stream)
|
||||
{
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-system_interface.html#FT_Stream_CloseFunc
|
||||
// Les streams dans Nazara ne se ferment pas explicitement
|
||||
NazaraUnused(stream);
|
||||
}
|
||||
|
||||
class FreeTypeLibrary
|
||||
{
|
||||
// Cette classe ne sert qu'à être utilisée avec un std::shared_ptr
|
||||
// pour ne libérer FreeType que lorsque plus personne ne l'utilise
|
||||
|
||||
public:
|
||||
FreeTypeLibrary() = default;
|
||||
~FreeTypeLibrary()
|
||||
// La valeur de count indique une opération de lecture ou de positionnement
|
||||
if (count > 0)
|
||||
{
|
||||
FT_Done_FreeType(s_library);
|
||||
s_library = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class FreeTypeStream : public NzFontData
|
||||
{
|
||||
public:
|
||||
FreeTypeStream() :
|
||||
m_face(nullptr),
|
||||
m_library(s_libraryOwner),
|
||||
m_characterSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
~FreeTypeStream()
|
||||
{
|
||||
if (m_face)
|
||||
FT_Done_Face(m_face);
|
||||
}
|
||||
|
||||
bool Check()
|
||||
{
|
||||
// Test d'ouverture (http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Open_Face)
|
||||
return FT_Open_Face(s_library, &m_args, -1, nullptr) == 0;
|
||||
}
|
||||
|
||||
bool ExtractGlyph(unsigned int characterSize, char32_t character, nzUInt32 style, NzFontGlyph* dst) override
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!dst)
|
||||
{
|
||||
NazaraError("Glyph destination cannot be null");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
if (FT_Load_Char(m_face, character, FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL) != 0)
|
||||
{
|
||||
NazaraError("Failed to load character");
|
||||
return false;
|
||||
}
|
||||
|
||||
FT_GlyphSlot& glyph = m_face->glyph;
|
||||
|
||||
const FT_Pos boldStrength = 2 << 6;
|
||||
|
||||
bool embolden = (style & nzTextStyle_Bold);
|
||||
|
||||
dst->advance = (embolden) ? boldStrength >> 6 : 0;
|
||||
|
||||
if (embolden && glyph->format == FT_GLYPH_FORMAT_OUTLINE)
|
||||
{
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-outline_processing.html#FT_Outline_Embolden
|
||||
FT_Outline_Embolden(&glyph->outline, boldStrength);
|
||||
embolden = false;
|
||||
}
|
||||
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-glyph_management.html#FT_Glyph_To_Bitmap
|
||||
// Conversion du glyphe vers le format bitmap
|
||||
// Cette fonction ne fait rien dans le cas où le glyphe est déjà un bitmap
|
||||
if (FT_Render_Glyph(glyph, FT_RENDER_MODE_NORMAL) != 0)
|
||||
{
|
||||
NazaraError("Failed to convert glyph to bitmap");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dans le cas où nous voulons des caractères gras mais que nous n'avons pas pu agir plus tôt
|
||||
// nous demandons à FreeType d'agir directement sur le bitmap généré
|
||||
if (embolden)
|
||||
{
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden
|
||||
// "If you want to embolden the bitmap owned by a FT_GlyphSlot_Rec, you should call FT_GlyphSlot_Own_Bitmap on the slot first"
|
||||
FT_GlyphSlot_Own_Bitmap(glyph);
|
||||
FT_Bitmap_Embolden(s_library, &glyph->bitmap, boldStrength, boldStrength);
|
||||
}
|
||||
|
||||
dst->advance += glyph->metrics.horiAdvance >> 6;
|
||||
dst->aabb.x = glyph->metrics.horiBearingX >> 6;
|
||||
dst->aabb.y = -(glyph->metrics.horiBearingY >> 6); // Inversion du repère
|
||||
dst->aabb.width = glyph->metrics.width >> 6;
|
||||
dst->aabb.height = glyph->metrics.height >> 6;
|
||||
|
||||
unsigned int width = glyph->bitmap.width;
|
||||
unsigned int height = glyph->bitmap.rows;
|
||||
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
dst->image.Create(nzImageType_2D, nzPixelFormat_A8, width, height);
|
||||
nzUInt8* pixels = dst->image.GetPixels();
|
||||
|
||||
const nzUInt8* data = glyph->bitmap.buffer;
|
||||
|
||||
// Selon la documentation FreeType, le glyphe peut être encodé en format A8 (huit bits d'alpha par pixel)
|
||||
// ou au format A1 (un bit d'alpha par pixel).
|
||||
// Cependant dans un cas comme dans l'autre, il nous faut gérer le pitch (les données peuvent ne pas être contigues)
|
||||
// ainsi que le padding dans le cas du format A1 (Chaque ligne prends un nombre fixe d'octets)
|
||||
if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
|
||||
{
|
||||
// Format A1
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
for (unsigned int x = 0; x < width; ++x)
|
||||
*pixels++ = (data[x/8] & ((1 << (7 - x%8)) ? 255 : 0));
|
||||
|
||||
data += glyph->bitmap.pitch;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Format A8
|
||||
if (glyph->bitmap.pitch == static_cast<int>(width*sizeof(nzUInt8))) // Pouvons-nous copier directement ?
|
||||
dst->image.Update(glyph->bitmap.buffer);
|
||||
else
|
||||
{
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
std::memcpy(pixels, data, width*sizeof(nzUInt8));
|
||||
data += glyph->bitmap.pitch;
|
||||
pixels += width*sizeof(nzUInt8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
dst->image.Destroy(); // On s'assure que l'image ne contient alors rien
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NzString GetFamilyName() const override
|
||||
{
|
||||
return m_face->family_name;
|
||||
}
|
||||
|
||||
NzString GetStyleName() const override
|
||||
{
|
||||
return m_face->style_name;
|
||||
}
|
||||
|
||||
bool HasKerning() const override
|
||||
{
|
||||
return FT_HAS_KERNING(m_face) != 0;
|
||||
}
|
||||
|
||||
bool IsScalable() const override
|
||||
{
|
||||
return FT_IS_SCALABLE(m_face) != 0;
|
||||
}
|
||||
|
||||
bool Open()
|
||||
{
|
||||
return FT_Open_Face(s_library, &m_args, 0, &m_face) == 0;
|
||||
}
|
||||
|
||||
int QueryKerning(unsigned int characterSize, char32_t first, char32_t second) const override
|
||||
{
|
||||
if (FT_HAS_KERNING(m_face))
|
||||
{
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
FT_Vector kerning;
|
||||
FT_Get_Kerning(m_face, FT_Get_Char_Index(m_face, first), FT_Get_Char_Index(m_face, second), FT_KERNING_DEFAULT, &kerning);
|
||||
|
||||
if (!FT_IS_SCALABLE(m_face))
|
||||
return kerning.x; // Taille déjà précisée en pixels dans ce cas
|
||||
|
||||
return kerning.x >> 6;
|
||||
}
|
||||
// Dans le premier cas, une erreur est symbolisée par un retour nul
|
||||
if (inputStream.SetCursorPos(offset))
|
||||
return static_cast<unsigned long>(inputStream.Read(buffer, count));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int QueryLineHeight(unsigned int characterSize) const override
|
||||
else
|
||||
{
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Size_Metrics
|
||||
return m_face->size->metrics.height >> 6;
|
||||
}
|
||||
|
||||
float QueryUnderlinePosition(unsigned int characterSize) const override
|
||||
{
|
||||
if (FT_IS_SCALABLE(m_face))
|
||||
{
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_FaceRec
|
||||
return static_cast<float>(FT_MulFix(m_face->underline_position, m_face->size->metrics.y_scale)) * s_invScaleFactor;
|
||||
}
|
||||
// Dans le second cas, une erreur est symbolisée par un retour non-nul
|
||||
if (inputStream.SetCursorPos(offset))
|
||||
return 0;
|
||||
else
|
||||
return characterSize / 10.f; // Joker ?
|
||||
return 42; // La réponse à la grande question
|
||||
}
|
||||
}
|
||||
|
||||
float QueryUnderlineThickness(unsigned int characterSize) const override
|
||||
{
|
||||
if (FT_IS_SCALABLE(m_face))
|
||||
extern "C"
|
||||
void FT_StreamClose(FT_Stream stream)
|
||||
{
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-system_interface.html#FT_Stream_CloseFunc
|
||||
// Les streams dans Nazara ne se ferment pas explicitement
|
||||
NazaraUnused(stream);
|
||||
}
|
||||
|
||||
class FreeTypeLibrary
|
||||
{
|
||||
// Cette classe ne sert qu'à être utilisée avec un std::shared_ptr
|
||||
// pour ne libérer FreeType que lorsque plus personne ne l'utilise
|
||||
|
||||
public:
|
||||
FreeTypeLibrary() = default;
|
||||
~FreeTypeLibrary()
|
||||
{
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_FaceRec
|
||||
return static_cast<float>(FT_MulFix(m_face->underline_thickness, m_face->size->metrics.y_scale)) * s_invScaleFactor;
|
||||
FT_Done_FreeType(s_library);
|
||||
s_library = nullptr;
|
||||
}
|
||||
else
|
||||
return characterSize/15.f; // Joker ?
|
||||
}
|
||||
|
||||
bool SetFile(const NzString& filePath)
|
||||
{
|
||||
std::unique_ptr<NzFile> file(new NzFile);
|
||||
if (!file->Open(filePath, nzOpenMode_ReadOnly))
|
||||
{
|
||||
NazaraError("Failed to open stream from file: " + NzError::GetLastError());
|
||||
return false;
|
||||
}
|
||||
m_ownedStream = std::move(file);
|
||||
|
||||
SetStream(*m_ownedStream);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetMemory(const void* data, std::size_t size)
|
||||
{
|
||||
m_ownedStream.reset(new NzMemoryStream(data, size));
|
||||
SetStream(*m_ownedStream);
|
||||
}
|
||||
|
||||
void SetStream(NzInputStream& stream)
|
||||
{
|
||||
m_stream.base = nullptr;
|
||||
m_stream.close = FT_StreamClose;
|
||||
m_stream.descriptor.pointer = &stream;
|
||||
m_stream.read = FT_StreamRead;
|
||||
m_stream.pos = 0;
|
||||
m_stream.size = static_cast<unsigned long>(stream.GetSize());
|
||||
|
||||
m_args.driver = 0;
|
||||
m_args.flags = FT_OPEN_STREAM;
|
||||
m_args.stream = &m_stream;
|
||||
}
|
||||
|
||||
bool SupportsStyle(nzUInt32 style) const override
|
||||
{
|
||||
///TODO
|
||||
return style == nzTextStyle_Regular || style == nzTextStyle_Bold;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetCharacterSize(unsigned int characterSize) const
|
||||
{
|
||||
if (m_characterSize != characterSize)
|
||||
{
|
||||
FT_Set_Pixel_Sizes(m_face, 0, characterSize);
|
||||
m_characterSize = characterSize;
|
||||
}
|
||||
}
|
||||
|
||||
FT_Open_Args m_args;
|
||||
FT_Face m_face;
|
||||
FT_StreamRec m_stream;
|
||||
std::shared_ptr<FreeTypeLibrary> m_library;
|
||||
std::unique_ptr<NzInputStream> m_ownedStream;
|
||||
mutable unsigned int m_characterSize;
|
||||
};
|
||||
|
||||
bool IsSupported(const NzString& extension)
|
||||
{
|
||||
///FIXME: Je suppose qu'il en manque quelques unes..
|
||||
static std::set<NzString> supportedExtensions = {
|
||||
"afm", "bdf", "cff", "cid", "dfont", "fnt", "fon", "otf", "pfa", "pfb", "pfm", "pfr", "sfnt", "ttc", "tte", "ttf"
|
||||
};
|
||||
|
||||
return supportedExtensions.find(extension) != supportedExtensions.end();
|
||||
class FreeTypeStream : public FontData
|
||||
{
|
||||
public:
|
||||
FreeTypeStream() :
|
||||
m_face(nullptr),
|
||||
m_library(s_libraryOwner),
|
||||
m_characterSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
~FreeTypeStream()
|
||||
{
|
||||
if (m_face)
|
||||
FT_Done_Face(m_face);
|
||||
}
|
||||
|
||||
bool Check()
|
||||
{
|
||||
// Test d'ouverture (http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Open_Face)
|
||||
return FT_Open_Face(s_library, &m_args, -1, nullptr) == 0;
|
||||
}
|
||||
|
||||
bool ExtractGlyph(unsigned int characterSize, char32_t character, UInt32 style, FontGlyph* dst) override
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!dst)
|
||||
{
|
||||
NazaraError("Glyph destination cannot be null");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
if (FT_Load_Char(m_face, character, FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL) != 0)
|
||||
{
|
||||
NazaraError("Failed to load character");
|
||||
return false;
|
||||
}
|
||||
|
||||
FT_GlyphSlot& glyph = m_face->glyph;
|
||||
|
||||
const FT_Pos boldStrength = 2 << 6;
|
||||
|
||||
bool embolden = (style & TextStyle_Bold);
|
||||
|
||||
dst->advance = (embolden) ? boldStrength >> 6 : 0;
|
||||
|
||||
if (embolden && glyph->format == FT_GLYPH_FORMAT_OUTLINE)
|
||||
{
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-outline_processing.html#FT_Outline_Embolden
|
||||
FT_Outline_Embolden(&glyph->outline, boldStrength);
|
||||
embolden = false;
|
||||
}
|
||||
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-glyph_management.html#FT_Glyph_To_Bitmap
|
||||
// Conversion du glyphe vers le format bitmap
|
||||
// Cette fonction ne fait rien dans le cas où le glyphe est déjà un bitmap
|
||||
if (FT_Render_Glyph(glyph, FT_RENDER_MODE_NORMAL) != 0)
|
||||
{
|
||||
NazaraError("Failed to convert glyph to bitmap");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dans le cas où nous voulons des caractères gras mais que nous n'avons pas pu agir plus tôt
|
||||
// nous demandons à FreeType d'agir directement sur le bitmap généré
|
||||
if (embolden)
|
||||
{
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden
|
||||
// "If you want to embolden the bitmap owned by a FT_GlyphSlot_Rec, you should call FT_GlyphSlot_Own_Bitmap on the slot first"
|
||||
FT_GlyphSlot_Own_Bitmap(glyph);
|
||||
FT_Bitmap_Embolden(s_library, &glyph->bitmap, boldStrength, boldStrength);
|
||||
}
|
||||
|
||||
dst->advance += glyph->metrics.horiAdvance >> 6;
|
||||
dst->aabb.x = glyph->metrics.horiBearingX >> 6;
|
||||
dst->aabb.y = -(glyph->metrics.horiBearingY >> 6); // Inversion du repère
|
||||
dst->aabb.width = glyph->metrics.width >> 6;
|
||||
dst->aabb.height = glyph->metrics.height >> 6;
|
||||
|
||||
unsigned int width = glyph->bitmap.width;
|
||||
unsigned int height = glyph->bitmap.rows;
|
||||
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
dst->image.Create(ImageType_2D, PixelFormatType_A8, width, height);
|
||||
UInt8* pixels = dst->image.GetPixels();
|
||||
|
||||
const UInt8* data = glyph->bitmap.buffer;
|
||||
|
||||
// Selon la documentation FreeType, le glyphe peut être encodé en format A8 (huit bits d'alpha par pixel)
|
||||
// ou au format A1 (un bit d'alpha par pixel).
|
||||
// Cependant dans un cas comme dans l'autre, il nous faut gérer le pitch (les données peuvent ne pas être contigues)
|
||||
// ainsi que le padding dans le cas du format A1 (Chaque ligne prends un nombre fixe d'octets)
|
||||
if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
|
||||
{
|
||||
// Format A1
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
for (unsigned int x = 0; x < width; ++x)
|
||||
*pixels++ = (data[x/8] & ((1 << (7 - x%8)) ? 255 : 0));
|
||||
|
||||
data += glyph->bitmap.pitch;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Format A8
|
||||
if (glyph->bitmap.pitch == static_cast<int>(width*sizeof(UInt8))) // Pouvons-nous copier directement ?
|
||||
dst->image.Update(glyph->bitmap.buffer);
|
||||
else
|
||||
{
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
std::memcpy(pixels, data, width*sizeof(UInt8));
|
||||
data += glyph->bitmap.pitch;
|
||||
pixels += width*sizeof(UInt8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
dst->image.Destroy(); // On s'assure que l'image ne contient alors rien
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String GetFamilyName() const override
|
||||
{
|
||||
return m_face->family_name;
|
||||
}
|
||||
|
||||
String GetStyleName() const override
|
||||
{
|
||||
return m_face->style_name;
|
||||
}
|
||||
|
||||
bool HasKerning() const override
|
||||
{
|
||||
return FT_HAS_KERNING(m_face) != 0;
|
||||
}
|
||||
|
||||
bool IsScalable() const override
|
||||
{
|
||||
return FT_IS_SCALABLE(m_face) != 0;
|
||||
}
|
||||
|
||||
bool Open()
|
||||
{
|
||||
return FT_Open_Face(s_library, &m_args, 0, &m_face) == 0;
|
||||
}
|
||||
|
||||
int QueryKerning(unsigned int characterSize, char32_t first, char32_t second) const override
|
||||
{
|
||||
if (FT_HAS_KERNING(m_face))
|
||||
{
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
FT_Vector kerning;
|
||||
FT_Get_Kerning(m_face, FT_Get_Char_Index(m_face, first), FT_Get_Char_Index(m_face, second), FT_KERNING_DEFAULT, &kerning);
|
||||
|
||||
if (!FT_IS_SCALABLE(m_face))
|
||||
return kerning.x; // Taille déjà précisée en pixels dans ce cas
|
||||
|
||||
return kerning.x >> 6;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int QueryLineHeight(unsigned int characterSize) const override
|
||||
{
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Size_Metrics
|
||||
return m_face->size->metrics.height >> 6;
|
||||
}
|
||||
|
||||
float QueryUnderlinePosition(unsigned int characterSize) const override
|
||||
{
|
||||
if (FT_IS_SCALABLE(m_face))
|
||||
{
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_FaceRec
|
||||
return static_cast<float>(FT_MulFix(m_face->underline_position, m_face->size->metrics.y_scale)) * s_invScaleFactor;
|
||||
}
|
||||
else
|
||||
return characterSize / 10.f; // Joker ?
|
||||
}
|
||||
|
||||
float QueryUnderlineThickness(unsigned int characterSize) const override
|
||||
{
|
||||
if (FT_IS_SCALABLE(m_face))
|
||||
{
|
||||
SetCharacterSize(characterSize);
|
||||
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_FaceRec
|
||||
return static_cast<float>(FT_MulFix(m_face->underline_thickness, m_face->size->metrics.y_scale)) * s_invScaleFactor;
|
||||
}
|
||||
else
|
||||
return characterSize/15.f; // Joker ?
|
||||
}
|
||||
|
||||
bool SetFile(const String& filePath)
|
||||
{
|
||||
std::unique_ptr<File> file(new File);
|
||||
if (!file->Open(filePath, OpenMode_ReadOnly))
|
||||
{
|
||||
NazaraError("Failed to open stream from file: " + Error::GetLastError());
|
||||
return false;
|
||||
}
|
||||
m_ownedStream = std::move(file);
|
||||
|
||||
SetStream(*m_ownedStream);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetMemory(const void* data, std::size_t size)
|
||||
{
|
||||
m_ownedStream.reset(new MemoryStream(data, size));
|
||||
SetStream(*m_ownedStream);
|
||||
}
|
||||
|
||||
void SetStream(InputStream& stream)
|
||||
{
|
||||
m_stream.base = nullptr;
|
||||
m_stream.close = FT_StreamClose;
|
||||
m_stream.descriptor.pointer = &stream;
|
||||
m_stream.read = FT_StreamRead;
|
||||
m_stream.pos = 0;
|
||||
m_stream.size = static_cast<unsigned long>(stream.GetSize());
|
||||
|
||||
m_args.driver = 0;
|
||||
m_args.flags = FT_OPEN_STREAM;
|
||||
m_args.stream = &m_stream;
|
||||
}
|
||||
|
||||
bool SupportsStyle(UInt32 style) const override
|
||||
{
|
||||
///TODO
|
||||
return style == TextStyle_Regular || style == TextStyle_Bold;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetCharacterSize(unsigned int characterSize) const
|
||||
{
|
||||
if (m_characterSize != characterSize)
|
||||
{
|
||||
FT_Set_Pixel_Sizes(m_face, 0, characterSize);
|
||||
m_characterSize = characterSize;
|
||||
}
|
||||
}
|
||||
|
||||
FT_Open_Args m_args;
|
||||
FT_Face m_face;
|
||||
FT_StreamRec m_stream;
|
||||
std::shared_ptr<FreeTypeLibrary> m_library;
|
||||
std::unique_ptr<InputStream> m_ownedStream;
|
||||
mutable unsigned int m_characterSize;
|
||||
};
|
||||
|
||||
bool IsSupported(const String& extension)
|
||||
{
|
||||
///FIXME: Je suppose qu'il en manque quelques unes..
|
||||
static std::set<String> supportedExtensions = {
|
||||
"afm", "bdf", "cff", "cid", "dfont", "fnt", "fon", "otf", "pfa", "pfb", "pfm", "pfr", "sfnt", "ttc", "tte", "ttf"
|
||||
};
|
||||
|
||||
return supportedExtensions.find(extension) != supportedExtensions.end();
|
||||
}
|
||||
|
||||
Ternary Check(InputStream& stream, const FontParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
FreeTypeStream face;
|
||||
face.SetStream(stream);
|
||||
|
||||
if (face.Check())
|
||||
return Ternary_True;
|
||||
else
|
||||
return Ternary_False;
|
||||
}
|
||||
|
||||
bool LoadFile(Font* font, const String& filePath, const FontParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
std::unique_ptr<FreeTypeStream> face(new FreeTypeStream);
|
||||
|
||||
if (!face->SetFile(filePath))
|
||||
{
|
||||
NazaraError("Failed to open file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!face->Open())
|
||||
{
|
||||
NazaraError("Failed to open face");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (font->Create(face.get()))
|
||||
{
|
||||
face.release();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LoadMemory(Font* font, const void* data, std::size_t size, const FontParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
std::unique_ptr<FreeTypeStream> face(new FreeTypeStream);
|
||||
face->SetMemory(data, size);
|
||||
|
||||
if (!face->Open())
|
||||
{
|
||||
NazaraError("Failed to open face");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (font->Create(face.get()))
|
||||
{
|
||||
face.release();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LoadStream(Font* font, InputStream& stream, const FontParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
std::unique_ptr<FreeTypeStream> face(new FreeTypeStream);
|
||||
face->SetStream(stream);
|
||||
|
||||
if (!face->Open())
|
||||
{
|
||||
NazaraError("Failed to open face");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (font->Create(face.get()))
|
||||
{
|
||||
face.release();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzFontParams& parameters)
|
||||
namespace Loaders
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
FreeTypeStream face;
|
||||
face.SetStream(stream);
|
||||
|
||||
if (face.Check())
|
||||
return nzTernary_True;
|
||||
else
|
||||
return nzTernary_False;
|
||||
}
|
||||
|
||||
bool LoadFile(NzFont* font, const NzString& filePath, const NzFontParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
std::unique_ptr<FreeTypeStream> face(new FreeTypeStream);
|
||||
|
||||
if (!face->SetFile(filePath))
|
||||
void RegisterFreeType()
|
||||
{
|
||||
NazaraError("Failed to open file");
|
||||
return false;
|
||||
if (FT_Init_FreeType(&s_library) == 0)
|
||||
{
|
||||
s_libraryOwner.reset(new FreeTypeLibrary);
|
||||
FontLoader::RegisterLoader(IsSupported, Check, LoadStream, LoadFile, LoadMemory);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_library = nullptr; // On s'assure que le pointeur ne pointe pas sur n'importe quoi
|
||||
NazaraWarning("Failed to initialize FreeType library");
|
||||
}
|
||||
}
|
||||
|
||||
if (!face->Open())
|
||||
void UnregisterFreeType()
|
||||
{
|
||||
NazaraError("Failed to open face");
|
||||
return false;
|
||||
if (s_library)
|
||||
{
|
||||
FontLoader::UnregisterLoader(IsSupported, Check, LoadStream, LoadFile, LoadMemory);
|
||||
s_libraryOwner.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (font->Create(face.get()))
|
||||
{
|
||||
face.release();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LoadMemory(NzFont* font, const void* data, std::size_t size, const NzFontParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
std::unique_ptr<FreeTypeStream> face(new FreeTypeStream);
|
||||
face->SetMemory(data, size);
|
||||
|
||||
if (!face->Open())
|
||||
{
|
||||
NazaraError("Failed to open face");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (font->Create(face.get()))
|
||||
{
|
||||
face.release();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LoadStream(NzFont* font, NzInputStream& stream, const NzFontParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
std::unique_ptr<FreeTypeStream> face(new FreeTypeStream);
|
||||
face->SetStream(stream);
|
||||
|
||||
if (!face->Open())
|
||||
{
|
||||
NazaraError("Failed to open face");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (font->Create(face.get()))
|
||||
{
|
||||
face.release();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_FreeType_Register()
|
||||
{
|
||||
if (FT_Init_FreeType(&s_library) == 0)
|
||||
{
|
||||
s_libraryOwner.reset(new FreeTypeLibrary);
|
||||
NzFontLoader::RegisterLoader(IsSupported, Check, LoadStream, LoadFile, LoadMemory);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_library = nullptr; // On s'assure que le pointeur ne pointe pas sur n'importe quoi
|
||||
NazaraWarning("Failed to initialize FreeType library");
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_FreeType_Unregister()
|
||||
{
|
||||
if (s_library)
|
||||
{
|
||||
NzFontLoader::UnregisterLoader(IsSupported, Check, LoadStream, LoadFile, LoadMemory);
|
||||
s_libraryOwner.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_FreeType_Register();
|
||||
void NzLoaders_FreeType_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterFreeType();
|
||||
void UnregisterFreeType();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_FREETYPE_HPP
|
||||
|
||||
@@ -5,170 +5,173 @@
|
||||
#include <Nazara/Utility/Formats/MD2Constants.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
const nzUInt32 md2Ident = 'I' + ('D'<<8) + ('P'<<16) + ('2'<<24);
|
||||
|
||||
const NzVector3f md2Normals[162] =
|
||||
namespace Nz
|
||||
{
|
||||
NzVector3f(-0.525731f, 0.000000f, 0.850651f),
|
||||
NzVector3f(-0.442863f, 0.238856f, 0.864188f),
|
||||
NzVector3f(-0.295242f, 0.000000f, 0.955423f),
|
||||
NzVector3f(-0.309017f, 0.500000f, 0.809017f),
|
||||
NzVector3f(-0.162460f, 0.262866f, 0.951056f),
|
||||
NzVector3f(0.000000f, 0.000000f, 1.000000f),
|
||||
NzVector3f(0.000000f, 0.850651f, 0.525731f),
|
||||
NzVector3f(-0.147621f, 0.716567f, 0.681718f),
|
||||
NzVector3f(0.147621f, 0.716567f, 0.681718f),
|
||||
NzVector3f(0.000000f, 0.525731f, 0.850651f),
|
||||
NzVector3f(0.309017f, 0.500000f, 0.809017f),
|
||||
NzVector3f(0.525731f, 0.000000f, 0.850651f),
|
||||
NzVector3f(0.295242f, 0.000000f, 0.955423f),
|
||||
NzVector3f(0.442863f, 0.238856f, 0.864188f),
|
||||
NzVector3f(0.162460f, 0.262866f, 0.951056f),
|
||||
NzVector3f(-0.681718f, 0.147621f, 0.716567f),
|
||||
NzVector3f(-0.809017f, 0.309017f, 0.500000f),
|
||||
NzVector3f(-0.587785f, 0.425325f, 0.688191f),
|
||||
NzVector3f(-0.850651f, 0.525731f, 0.000000f),
|
||||
NzVector3f(-0.864188f, 0.442863f, 0.238856f),
|
||||
NzVector3f(-0.716567f, 0.681718f, 0.147621f),
|
||||
NzVector3f(-0.688191f, 0.587785f, 0.425325f),
|
||||
NzVector3f(-0.500000f, 0.809017f, 0.309017f),
|
||||
NzVector3f(-0.238856f, 0.864188f, 0.442863f),
|
||||
NzVector3f(-0.425325f, 0.688191f, 0.587785f),
|
||||
NzVector3f(-0.716567f, 0.681718f, -0.147621f),
|
||||
NzVector3f(-0.500000f, 0.809017f, -0.309017f),
|
||||
NzVector3f(-0.525731f, 0.850651f, 0.000000f),
|
||||
NzVector3f(0.000000f, 0.850651f, -0.525731f),
|
||||
NzVector3f(-0.238856f, 0.864188f, -0.442863f),
|
||||
NzVector3f(0.000000f, 0.955423f, -0.295242f),
|
||||
NzVector3f(-0.262866f, 0.951056f, -0.162460f),
|
||||
NzVector3f(0.000000f, 1.000000f, 0.000000f),
|
||||
NzVector3f(0.000000f, 0.955423f, 0.295242f),
|
||||
NzVector3f(-0.262866f, 0.951056f, 0.162460f),
|
||||
NzVector3f(0.238856f, 0.864188f, 0.442863f),
|
||||
NzVector3f(0.262866f, 0.951056f, 0.162460f),
|
||||
NzVector3f(0.500000f, 0.809017f, 0.309017f),
|
||||
NzVector3f(0.238856f, 0.864188f, -0.442863f),
|
||||
NzVector3f(0.262866f, 0.951056f, -0.162460f),
|
||||
NzVector3f(0.500000f, 0.809017f, -0.309017f),
|
||||
NzVector3f(0.850651f, 0.525731f, 0.000000f),
|
||||
NzVector3f(0.716567f, 0.681718f, 0.147621f),
|
||||
NzVector3f(0.716567f, 0.681718f, -0.147621f),
|
||||
NzVector3f(0.525731f, 0.850651f, 0.000000f),
|
||||
NzVector3f(0.425325f, 0.688191f, 0.587785f),
|
||||
NzVector3f(0.864188f, 0.442863f, 0.238856f),
|
||||
NzVector3f(0.688191f, 0.587785f, 0.425325f),
|
||||
NzVector3f(0.809017f, 0.309017f, 0.500000f),
|
||||
NzVector3f(0.681718f, 0.147621f, 0.716567f),
|
||||
NzVector3f(0.587785f, 0.425325f, 0.688191f),
|
||||
NzVector3f(0.955423f, 0.295242f, 0.000000f),
|
||||
NzVector3f(1.000000f, 0.000000f, 0.000000f),
|
||||
NzVector3f(0.951056f, 0.162460f, 0.262866f),
|
||||
NzVector3f(0.850651f, -0.525731f, 0.000000f),
|
||||
NzVector3f(0.955423f, -0.295242f, 0.000000f),
|
||||
NzVector3f(0.864188f, -0.442863f, 0.238856f),
|
||||
NzVector3f(0.951056f, -0.162460f, 0.262866f),
|
||||
NzVector3f(0.809017f, -0.309017f, 0.500000f),
|
||||
NzVector3f(0.681718f, -0.147621f, 0.716567f),
|
||||
NzVector3f(0.850651f, 0.000000f, 0.525731f),
|
||||
NzVector3f(0.864188f, 0.442863f, -0.238856f),
|
||||
NzVector3f(0.809017f, 0.309017f, -0.500000f),
|
||||
NzVector3f(0.951056f, 0.162460f, -0.262866f),
|
||||
NzVector3f(0.525731f, 0.000000f, -0.850651f),
|
||||
NzVector3f(0.681718f, 0.147621f, -0.716567f),
|
||||
NzVector3f(0.681718f, -0.147621f, -0.716567f),
|
||||
NzVector3f(0.850651f, 0.000000f, -0.525731f),
|
||||
NzVector3f(0.809017f, -0.309017f, -0.500000f),
|
||||
NzVector3f(0.864188f, -0.442863f, -0.238856f),
|
||||
NzVector3f(0.951056f, -0.162460f, -0.262866f),
|
||||
NzVector3f(0.147621f, 0.716567f, -0.681718f),
|
||||
NzVector3f(0.309017f, 0.500000f, -0.809017f),
|
||||
NzVector3f(0.425325f, 0.688191f, -0.587785f),
|
||||
NzVector3f(0.442863f, 0.238856f, -0.864188f),
|
||||
NzVector3f(0.587785f, 0.425325f, -0.688191f),
|
||||
NzVector3f(0.688191f, 0.587785f, -0.425325f),
|
||||
NzVector3f(-0.147621f, 0.716567f, -0.681718f),
|
||||
NzVector3f(-0.309017f, 0.500000f, -0.809017f),
|
||||
NzVector3f(0.000000f, 0.525731f, -0.850651f),
|
||||
NzVector3f(-0.525731f, 0.000000f, -0.850651f),
|
||||
NzVector3f(-0.442863f, 0.238856f, -0.864188f),
|
||||
NzVector3f(-0.295242f, 0.000000f, -0.955423f),
|
||||
NzVector3f(-0.162460f, 0.262866f, -0.951056f),
|
||||
NzVector3f(0.000000f, 0.000000f, -1.000000f),
|
||||
NzVector3f(0.295242f, 0.000000f, -0.955423f),
|
||||
NzVector3f(0.162460f, 0.262866f, -0.951056f),
|
||||
NzVector3f(-0.442863f, -0.238856f, -0.864188f),
|
||||
NzVector3f(-0.309017f, -0.500000f, -0.809017f),
|
||||
NzVector3f(-0.162460f, -0.262866f, -0.951056f),
|
||||
NzVector3f(0.000000f, -0.850651f, -0.525731f),
|
||||
NzVector3f(-0.147621f, -0.716567f, -0.681718f),
|
||||
NzVector3f(0.147621f, -0.716567f, -0.681718f),
|
||||
NzVector3f(0.000000f, -0.525731f, -0.850651f),
|
||||
NzVector3f(0.309017f, -0.500000f, -0.809017f),
|
||||
NzVector3f(0.442863f, -0.238856f, -0.864188f),
|
||||
NzVector3f(0.162460f, -0.262866f, -0.951056f),
|
||||
NzVector3f(0.238856f, -0.864188f, -0.442863f),
|
||||
NzVector3f(0.500000f, -0.809017f, -0.309017f),
|
||||
NzVector3f(0.425325f, -0.688191f, -0.587785f),
|
||||
NzVector3f(0.716567f, -0.681718f, -0.147621f),
|
||||
NzVector3f(0.688191f, -0.587785f, -0.425325f),
|
||||
NzVector3f(0.587785f, -0.425325f, -0.688191f),
|
||||
NzVector3f(0.000000f, -0.955423f, -0.295242f),
|
||||
NzVector3f(0.000000f, -1.000000f, 0.000000f),
|
||||
NzVector3f(0.262866f, -0.951056f, -0.162460f),
|
||||
NzVector3f(0.000000f, -0.850651f, 0.525731f),
|
||||
NzVector3f(0.000000f, -0.955423f, 0.295242f),
|
||||
NzVector3f(0.238856f, -0.864188f, 0.442863f),
|
||||
NzVector3f(0.262866f, -0.951056f, 0.162460f),
|
||||
NzVector3f(0.500000f, -0.809017f, 0.309017f),
|
||||
NzVector3f(0.716567f, -0.681718f, 0.147621f),
|
||||
NzVector3f(0.525731f, -0.850651f, 0.000000f),
|
||||
NzVector3f(-0.238856f, -0.864188f, -0.442863f),
|
||||
NzVector3f(-0.500000f, -0.809017f, -0.309017f),
|
||||
NzVector3f(-0.262866f, -0.951056f, -0.162460f),
|
||||
NzVector3f(-0.850651f, -0.525731f, 0.000000f),
|
||||
NzVector3f(-0.716567f, -0.681718f, -0.147621f),
|
||||
NzVector3f(-0.716567f, -0.681718f, 0.147621f),
|
||||
NzVector3f(-0.525731f, -0.850651f, 0.000000f),
|
||||
NzVector3f(-0.500000f, -0.809017f, 0.309017f),
|
||||
NzVector3f(-0.238856f, -0.864188f, 0.442863f),
|
||||
NzVector3f(-0.262866f, -0.951056f, 0.162460f),
|
||||
NzVector3f(-0.864188f, -0.442863f, 0.238856f),
|
||||
NzVector3f(-0.809017f, -0.309017f, 0.500000f),
|
||||
NzVector3f(-0.688191f, -0.587785f, 0.425325f),
|
||||
NzVector3f(-0.681718f, -0.147621f, 0.716567f),
|
||||
NzVector3f(-0.442863f, -0.238856f, 0.864188f),
|
||||
NzVector3f(-0.587785f, -0.425325f, 0.688191f),
|
||||
NzVector3f(-0.309017f, -0.500000f, 0.809017f),
|
||||
NzVector3f(-0.147621f, -0.716567f, 0.681718f),
|
||||
NzVector3f(-0.425325f, -0.688191f, 0.587785f),
|
||||
NzVector3f(-0.162460f, -0.262866f, 0.951056f),
|
||||
NzVector3f(0.442863f, -0.238856f, 0.864188f),
|
||||
NzVector3f(0.162460f, -0.262866f, 0.951056f),
|
||||
NzVector3f(0.309017f, -0.500000f, 0.809017f),
|
||||
NzVector3f(0.147621f, -0.716567f, 0.681718f),
|
||||
NzVector3f(0.000000f, -0.525731f, 0.850651f),
|
||||
NzVector3f(0.425325f, -0.688191f, 0.587785f),
|
||||
NzVector3f(0.587785f, -0.425325f, 0.688191f),
|
||||
NzVector3f(0.688191f, -0.587785f, 0.425325f),
|
||||
NzVector3f(-0.955423f, 0.295242f, 0.000000f),
|
||||
NzVector3f(-0.951056f, 0.162460f, 0.262866f),
|
||||
NzVector3f(-1.000000f, 0.000000f, 0.000000f),
|
||||
NzVector3f(-0.850651f, 0.000000f, 0.525731f),
|
||||
NzVector3f(-0.955423f, -0.295242f, 0.000000f),
|
||||
NzVector3f(-0.951056f, -0.162460f, 0.262866f),
|
||||
NzVector3f(-0.864188f, 0.442863f, -0.238856f),
|
||||
NzVector3f(-0.951056f, 0.162460f, -0.262866f),
|
||||
NzVector3f(-0.809017f, 0.309017f, -0.500000f),
|
||||
NzVector3f(-0.864188f, -0.442863f, -0.238856f),
|
||||
NzVector3f(-0.951056f, -0.162460f, -0.262866f),
|
||||
NzVector3f(-0.809017f, -0.309017f, -0.500000f),
|
||||
NzVector3f(-0.681718f, 0.147621f, -0.716567f),
|
||||
NzVector3f(-0.681718f, -0.147621f, -0.716567f),
|
||||
NzVector3f(-0.850651f, 0.000000f, -0.525731f),
|
||||
NzVector3f(-0.688191f, 0.587785f, -0.425325f),
|
||||
NzVector3f(-0.587785f, 0.425325f, -0.688191f),
|
||||
NzVector3f(-0.425325f, 0.688191f, -0.587785f),
|
||||
NzVector3f(-0.425325f, -0.688191f, -0.587785f),
|
||||
NzVector3f(-0.587785f, -0.425325f, -0.688191f),
|
||||
NzVector3f(-0.688191f, -0.587785f, -0.425325f)
|
||||
};
|
||||
const UInt32 md2Ident = 'I' + ('D'<<8) + ('P'<<16) + ('2'<<24);
|
||||
|
||||
const Vector3f md2Normals[162] =
|
||||
{
|
||||
Vector3f(-0.525731f, 0.000000f, 0.850651f),
|
||||
Vector3f(-0.442863f, 0.238856f, 0.864188f),
|
||||
Vector3f(-0.295242f, 0.000000f, 0.955423f),
|
||||
Vector3f(-0.309017f, 0.500000f, 0.809017f),
|
||||
Vector3f(-0.162460f, 0.262866f, 0.951056f),
|
||||
Vector3f(0.000000f, 0.000000f, 1.000000f),
|
||||
Vector3f(0.000000f, 0.850651f, 0.525731f),
|
||||
Vector3f(-0.147621f, 0.716567f, 0.681718f),
|
||||
Vector3f(0.147621f, 0.716567f, 0.681718f),
|
||||
Vector3f(0.000000f, 0.525731f, 0.850651f),
|
||||
Vector3f(0.309017f, 0.500000f, 0.809017f),
|
||||
Vector3f(0.525731f, 0.000000f, 0.850651f),
|
||||
Vector3f(0.295242f, 0.000000f, 0.955423f),
|
||||
Vector3f(0.442863f, 0.238856f, 0.864188f),
|
||||
Vector3f(0.162460f, 0.262866f, 0.951056f),
|
||||
Vector3f(-0.681718f, 0.147621f, 0.716567f),
|
||||
Vector3f(-0.809017f, 0.309017f, 0.500000f),
|
||||
Vector3f(-0.587785f, 0.425325f, 0.688191f),
|
||||
Vector3f(-0.850651f, 0.525731f, 0.000000f),
|
||||
Vector3f(-0.864188f, 0.442863f, 0.238856f),
|
||||
Vector3f(-0.716567f, 0.681718f, 0.147621f),
|
||||
Vector3f(-0.688191f, 0.587785f, 0.425325f),
|
||||
Vector3f(-0.500000f, 0.809017f, 0.309017f),
|
||||
Vector3f(-0.238856f, 0.864188f, 0.442863f),
|
||||
Vector3f(-0.425325f, 0.688191f, 0.587785f),
|
||||
Vector3f(-0.716567f, 0.681718f, -0.147621f),
|
||||
Vector3f(-0.500000f, 0.809017f, -0.309017f),
|
||||
Vector3f(-0.525731f, 0.850651f, 0.000000f),
|
||||
Vector3f(0.000000f, 0.850651f, -0.525731f),
|
||||
Vector3f(-0.238856f, 0.864188f, -0.442863f),
|
||||
Vector3f(0.000000f, 0.955423f, -0.295242f),
|
||||
Vector3f(-0.262866f, 0.951056f, -0.162460f),
|
||||
Vector3f(0.000000f, 1.000000f, 0.000000f),
|
||||
Vector3f(0.000000f, 0.955423f, 0.295242f),
|
||||
Vector3f(-0.262866f, 0.951056f, 0.162460f),
|
||||
Vector3f(0.238856f, 0.864188f, 0.442863f),
|
||||
Vector3f(0.262866f, 0.951056f, 0.162460f),
|
||||
Vector3f(0.500000f, 0.809017f, 0.309017f),
|
||||
Vector3f(0.238856f, 0.864188f, -0.442863f),
|
||||
Vector3f(0.262866f, 0.951056f, -0.162460f),
|
||||
Vector3f(0.500000f, 0.809017f, -0.309017f),
|
||||
Vector3f(0.850651f, 0.525731f, 0.000000f),
|
||||
Vector3f(0.716567f, 0.681718f, 0.147621f),
|
||||
Vector3f(0.716567f, 0.681718f, -0.147621f),
|
||||
Vector3f(0.525731f, 0.850651f, 0.000000f),
|
||||
Vector3f(0.425325f, 0.688191f, 0.587785f),
|
||||
Vector3f(0.864188f, 0.442863f, 0.238856f),
|
||||
Vector3f(0.688191f, 0.587785f, 0.425325f),
|
||||
Vector3f(0.809017f, 0.309017f, 0.500000f),
|
||||
Vector3f(0.681718f, 0.147621f, 0.716567f),
|
||||
Vector3f(0.587785f, 0.425325f, 0.688191f),
|
||||
Vector3f(0.955423f, 0.295242f, 0.000000f),
|
||||
Vector3f(1.000000f, 0.000000f, 0.000000f),
|
||||
Vector3f(0.951056f, 0.162460f, 0.262866f),
|
||||
Vector3f(0.850651f, -0.525731f, 0.000000f),
|
||||
Vector3f(0.955423f, -0.295242f, 0.000000f),
|
||||
Vector3f(0.864188f, -0.442863f, 0.238856f),
|
||||
Vector3f(0.951056f, -0.162460f, 0.262866f),
|
||||
Vector3f(0.809017f, -0.309017f, 0.500000f),
|
||||
Vector3f(0.681718f, -0.147621f, 0.716567f),
|
||||
Vector3f(0.850651f, 0.000000f, 0.525731f),
|
||||
Vector3f(0.864188f, 0.442863f, -0.238856f),
|
||||
Vector3f(0.809017f, 0.309017f, -0.500000f),
|
||||
Vector3f(0.951056f, 0.162460f, -0.262866f),
|
||||
Vector3f(0.525731f, 0.000000f, -0.850651f),
|
||||
Vector3f(0.681718f, 0.147621f, -0.716567f),
|
||||
Vector3f(0.681718f, -0.147621f, -0.716567f),
|
||||
Vector3f(0.850651f, 0.000000f, -0.525731f),
|
||||
Vector3f(0.809017f, -0.309017f, -0.500000f),
|
||||
Vector3f(0.864188f, -0.442863f, -0.238856f),
|
||||
Vector3f(0.951056f, -0.162460f, -0.262866f),
|
||||
Vector3f(0.147621f, 0.716567f, -0.681718f),
|
||||
Vector3f(0.309017f, 0.500000f, -0.809017f),
|
||||
Vector3f(0.425325f, 0.688191f, -0.587785f),
|
||||
Vector3f(0.442863f, 0.238856f, -0.864188f),
|
||||
Vector3f(0.587785f, 0.425325f, -0.688191f),
|
||||
Vector3f(0.688191f, 0.587785f, -0.425325f),
|
||||
Vector3f(-0.147621f, 0.716567f, -0.681718f),
|
||||
Vector3f(-0.309017f, 0.500000f, -0.809017f),
|
||||
Vector3f(0.000000f, 0.525731f, -0.850651f),
|
||||
Vector3f(-0.525731f, 0.000000f, -0.850651f),
|
||||
Vector3f(-0.442863f, 0.238856f, -0.864188f),
|
||||
Vector3f(-0.295242f, 0.000000f, -0.955423f),
|
||||
Vector3f(-0.162460f, 0.262866f, -0.951056f),
|
||||
Vector3f(0.000000f, 0.000000f, -1.000000f),
|
||||
Vector3f(0.295242f, 0.000000f, -0.955423f),
|
||||
Vector3f(0.162460f, 0.262866f, -0.951056f),
|
||||
Vector3f(-0.442863f, -0.238856f, -0.864188f),
|
||||
Vector3f(-0.309017f, -0.500000f, -0.809017f),
|
||||
Vector3f(-0.162460f, -0.262866f, -0.951056f),
|
||||
Vector3f(0.000000f, -0.850651f, -0.525731f),
|
||||
Vector3f(-0.147621f, -0.716567f, -0.681718f),
|
||||
Vector3f(0.147621f, -0.716567f, -0.681718f),
|
||||
Vector3f(0.000000f, -0.525731f, -0.850651f),
|
||||
Vector3f(0.309017f, -0.500000f, -0.809017f),
|
||||
Vector3f(0.442863f, -0.238856f, -0.864188f),
|
||||
Vector3f(0.162460f, -0.262866f, -0.951056f),
|
||||
Vector3f(0.238856f, -0.864188f, -0.442863f),
|
||||
Vector3f(0.500000f, -0.809017f, -0.309017f),
|
||||
Vector3f(0.425325f, -0.688191f, -0.587785f),
|
||||
Vector3f(0.716567f, -0.681718f, -0.147621f),
|
||||
Vector3f(0.688191f, -0.587785f, -0.425325f),
|
||||
Vector3f(0.587785f, -0.425325f, -0.688191f),
|
||||
Vector3f(0.000000f, -0.955423f, -0.295242f),
|
||||
Vector3f(0.000000f, -1.000000f, 0.000000f),
|
||||
Vector3f(0.262866f, -0.951056f, -0.162460f),
|
||||
Vector3f(0.000000f, -0.850651f, 0.525731f),
|
||||
Vector3f(0.000000f, -0.955423f, 0.295242f),
|
||||
Vector3f(0.238856f, -0.864188f, 0.442863f),
|
||||
Vector3f(0.262866f, -0.951056f, 0.162460f),
|
||||
Vector3f(0.500000f, -0.809017f, 0.309017f),
|
||||
Vector3f(0.716567f, -0.681718f, 0.147621f),
|
||||
Vector3f(0.525731f, -0.850651f, 0.000000f),
|
||||
Vector3f(-0.238856f, -0.864188f, -0.442863f),
|
||||
Vector3f(-0.500000f, -0.809017f, -0.309017f),
|
||||
Vector3f(-0.262866f, -0.951056f, -0.162460f),
|
||||
Vector3f(-0.850651f, -0.525731f, 0.000000f),
|
||||
Vector3f(-0.716567f, -0.681718f, -0.147621f),
|
||||
Vector3f(-0.716567f, -0.681718f, 0.147621f),
|
||||
Vector3f(-0.525731f, -0.850651f, 0.000000f),
|
||||
Vector3f(-0.500000f, -0.809017f, 0.309017f),
|
||||
Vector3f(-0.238856f, -0.864188f, 0.442863f),
|
||||
Vector3f(-0.262866f, -0.951056f, 0.162460f),
|
||||
Vector3f(-0.864188f, -0.442863f, 0.238856f),
|
||||
Vector3f(-0.809017f, -0.309017f, 0.500000f),
|
||||
Vector3f(-0.688191f, -0.587785f, 0.425325f),
|
||||
Vector3f(-0.681718f, -0.147621f, 0.716567f),
|
||||
Vector3f(-0.442863f, -0.238856f, 0.864188f),
|
||||
Vector3f(-0.587785f, -0.425325f, 0.688191f),
|
||||
Vector3f(-0.309017f, -0.500000f, 0.809017f),
|
||||
Vector3f(-0.147621f, -0.716567f, 0.681718f),
|
||||
Vector3f(-0.425325f, -0.688191f, 0.587785f),
|
||||
Vector3f(-0.162460f, -0.262866f, 0.951056f),
|
||||
Vector3f(0.442863f, -0.238856f, 0.864188f),
|
||||
Vector3f(0.162460f, -0.262866f, 0.951056f),
|
||||
Vector3f(0.309017f, -0.500000f, 0.809017f),
|
||||
Vector3f(0.147621f, -0.716567f, 0.681718f),
|
||||
Vector3f(0.000000f, -0.525731f, 0.850651f),
|
||||
Vector3f(0.425325f, -0.688191f, 0.587785f),
|
||||
Vector3f(0.587785f, -0.425325f, 0.688191f),
|
||||
Vector3f(0.688191f, -0.587785f, 0.425325f),
|
||||
Vector3f(-0.955423f, 0.295242f, 0.000000f),
|
||||
Vector3f(-0.951056f, 0.162460f, 0.262866f),
|
||||
Vector3f(-1.000000f, 0.000000f, 0.000000f),
|
||||
Vector3f(-0.850651f, 0.000000f, 0.525731f),
|
||||
Vector3f(-0.955423f, -0.295242f, 0.000000f),
|
||||
Vector3f(-0.951056f, -0.162460f, 0.262866f),
|
||||
Vector3f(-0.864188f, 0.442863f, -0.238856f),
|
||||
Vector3f(-0.951056f, 0.162460f, -0.262866f),
|
||||
Vector3f(-0.809017f, 0.309017f, -0.500000f),
|
||||
Vector3f(-0.864188f, -0.442863f, -0.238856f),
|
||||
Vector3f(-0.951056f, -0.162460f, -0.262866f),
|
||||
Vector3f(-0.809017f, -0.309017f, -0.500000f),
|
||||
Vector3f(-0.681718f, 0.147621f, -0.716567f),
|
||||
Vector3f(-0.681718f, -0.147621f, -0.716567f),
|
||||
Vector3f(-0.850651f, 0.000000f, -0.525731f),
|
||||
Vector3f(-0.688191f, 0.587785f, -0.425325f),
|
||||
Vector3f(-0.587785f, 0.425325f, -0.688191f),
|
||||
Vector3f(-0.425325f, 0.688191f, -0.587785f),
|
||||
Vector3f(-0.425325f, -0.688191f, -0.587785f),
|
||||
Vector3f(-0.587785f, -0.425325f, -0.688191f),
|
||||
Vector3f(-0.688191f, -0.587785f, -0.425325f)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,57 +8,60 @@
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
|
||||
struct MD2_Header
|
||||
namespace Nz
|
||||
{
|
||||
nzUInt32 ident; // nombre magique : "IDP2"
|
||||
nzUInt32 version; // version du format : 8
|
||||
struct MD2_Header
|
||||
{
|
||||
UInt32 ident; // nombre magique : "IDP2"
|
||||
UInt32 version; // version du format : 8
|
||||
|
||||
nzUInt32 skinwidth; // largeur texture
|
||||
nzUInt32 skinheight; // hauteur texture
|
||||
UInt32 skinwidth; // largeur texture
|
||||
UInt32 skinheight; // hauteur texture
|
||||
|
||||
nzUInt32 framesize; // taille d'une frame en octets
|
||||
UInt32 framesize; // taille d'une frame en octets
|
||||
|
||||
nzUInt32 num_skins; // nombre de skins
|
||||
nzUInt32 num_vertices; // nombre de vertices par frame
|
||||
nzUInt32 num_st; // nombre de coordonnées de texture
|
||||
nzUInt32 num_tris; // nombre de triangles
|
||||
nzUInt32 num_glcmds; // nombre de commandes opengl
|
||||
nzUInt32 num_frames; // nombre de frames
|
||||
UInt32 num_skins; // nombre de skins
|
||||
UInt32 num_vertices; // nombre de vertices par frame
|
||||
UInt32 num_st; // nombre de coordonnées de texture
|
||||
UInt32 num_tris; // nombre de triangles
|
||||
UInt32 num_glcmds; // nombre de commandes opengl
|
||||
UInt32 num_frames; // nombre de frames
|
||||
|
||||
nzUInt32 offset_skins; // offset données skins
|
||||
nzUInt32 offset_st; // offset données coordonnées de texture
|
||||
nzUInt32 offset_tris; // offset données triangles
|
||||
nzUInt32 offset_frames; // offset données frames
|
||||
nzUInt32 offset_glcmds; // offset données commandes OpenGL
|
||||
nzUInt32 offset_end; // offset fin de fichier
|
||||
};
|
||||
UInt32 offset_skins; // offset données skins
|
||||
UInt32 offset_st; // offset données coordonnées de texture
|
||||
UInt32 offset_tris; // offset données triangles
|
||||
UInt32 offset_frames; // offset données frames
|
||||
UInt32 offset_glcmds; // offset données commandes OpenGL
|
||||
UInt32 offset_end; // offset fin de fichier
|
||||
};
|
||||
|
||||
static_assert(sizeof(MD2_Header) == 17*sizeof(nzUInt32), "MD2_Header must be packed");
|
||||
static_assert(sizeof(MD2_Header) == 17*sizeof(UInt32), "MD2_Header must be packed");
|
||||
|
||||
struct MD2_Vertex
|
||||
{
|
||||
nzUInt8 x, y, z;
|
||||
nzUInt8 n;
|
||||
};
|
||||
struct MD2_Vertex
|
||||
{
|
||||
UInt8 x, y, z;
|
||||
UInt8 n;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MD2_Vertex) == 4*sizeof(nzUInt8), "MD2_Vertex must be packed");
|
||||
static_assert(sizeof(MD2_Vertex) == 4*sizeof(UInt8), "MD2_Vertex must be packed");
|
||||
|
||||
struct MD2_TexCoord
|
||||
{
|
||||
nzInt16 u, v;
|
||||
};
|
||||
struct MD2_TexCoord
|
||||
{
|
||||
Int16 u, v;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MD2_TexCoord) == 2*sizeof(nzInt16), "MD2_TexCoord must be packed");
|
||||
static_assert(sizeof(MD2_TexCoord) == 2*sizeof(Int16), "MD2_TexCoord must be packed");
|
||||
|
||||
struct MD2_Triangle
|
||||
{
|
||||
nzUInt16 vertices[3];
|
||||
nzUInt16 texCoords[3];
|
||||
};
|
||||
struct MD2_Triangle
|
||||
{
|
||||
UInt16 vertices[3];
|
||||
UInt16 texCoords[3];
|
||||
};
|
||||
|
||||
static_assert(sizeof(MD2_Triangle) == 2*3*sizeof(nzUInt16), "MD2_Triangle must be packed");
|
||||
static_assert(sizeof(MD2_Triangle) == 2*3*sizeof(UInt16), "MD2_Triangle must be packed");
|
||||
|
||||
extern const nzUInt32 md2Ident;
|
||||
extern const NzVector3f md2Normals[162];
|
||||
extern const UInt32 md2Ident;
|
||||
extern const Vector3f md2Normals[162];
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_MD2_CONSTANTS_HPP
|
||||
|
||||
@@ -17,230 +17,236 @@
|
||||
#include <memory>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
bool IsSupported(const NzString& extension)
|
||||
namespace
|
||||
{
|
||||
return (extension == "md2");
|
||||
}
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzMeshParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
nzUInt32 magic[2];
|
||||
if (stream.Read(&magic[0], 2*sizeof(nzUInt32)) == 2*sizeof(nzUInt32))
|
||||
bool IsSupported(const String& extension)
|
||||
{
|
||||
return (extension == "md2");
|
||||
}
|
||||
|
||||
Ternary Check(InputStream& stream, const MeshParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
UInt32 magic[2];
|
||||
if (stream.Read(&magic[0], 2*sizeof(UInt32)) == 2*sizeof(UInt32))
|
||||
{
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
SwapBytes(&magic[0], sizeof(UInt32));
|
||||
SwapBytes(&magic[1], sizeof(UInt32));
|
||||
#endif
|
||||
|
||||
if (magic[0] == md2Ident && magic[1] == 8)
|
||||
return Ternary_True;
|
||||
}
|
||||
|
||||
return Ternary_False;
|
||||
}
|
||||
|
||||
bool Load(Mesh* mesh, InputStream& stream, const MeshParams& parameters)
|
||||
{
|
||||
MD2_Header header;
|
||||
if (stream.Read(&header, sizeof(MD2_Header)) != sizeof(MD2_Header))
|
||||
{
|
||||
NazaraError("Failed to read header");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(&magic[0], sizeof(nzUInt32));
|
||||
NzByteSwap(&magic[1], sizeof(nzUInt32));
|
||||
SwapBytes(&header.skinwidth, sizeof(UInt32));
|
||||
SwapBytes(&header.skinheight, sizeof(UInt32));
|
||||
SwapBytes(&header.framesize, sizeof(UInt32));
|
||||
SwapBytes(&header.num_skins, sizeof(UInt32));
|
||||
SwapBytes(&header.num_vertices, sizeof(UInt32));
|
||||
SwapBytes(&header.num_st, sizeof(UInt32));
|
||||
SwapBytes(&header.num_tris, sizeof(UInt32));
|
||||
SwapBytes(&header.num_glcmds, sizeof(UInt32));
|
||||
SwapBytes(&header.num_frames, sizeof(UInt32));
|
||||
SwapBytes(&header.offset_skins, sizeof(UInt32));
|
||||
SwapBytes(&header.offset_st, sizeof(UInt32));
|
||||
SwapBytes(&header.offset_tris, sizeof(UInt32));
|
||||
SwapBytes(&header.offset_frames, sizeof(UInt32));
|
||||
SwapBytes(&header.offset_glcmds, sizeof(UInt32));
|
||||
SwapBytes(&header.offset_end, sizeof(UInt32));
|
||||
#endif
|
||||
|
||||
if (magic[0] == md2Ident && magic[1] == 8)
|
||||
return nzTernary_True;
|
||||
}
|
||||
|
||||
return nzTernary_False;
|
||||
}
|
||||
|
||||
bool Load(NzMesh* mesh, NzInputStream& stream, const NzMeshParams& parameters)
|
||||
{
|
||||
MD2_Header header;
|
||||
if (stream.Read(&header, sizeof(MD2_Header)) != sizeof(MD2_Header))
|
||||
{
|
||||
NazaraError("Failed to read header");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(&header.skinwidth, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.skinheight, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.framesize, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.num_skins, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.num_vertices, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.num_st, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.num_tris, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.num_glcmds, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.num_frames, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.offset_skins, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.offset_st, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.offset_tris, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.offset_frames, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.offset_glcmds, sizeof(nzUInt32));
|
||||
NzByteSwap(&header.offset_end, sizeof(nzUInt32));
|
||||
#endif
|
||||
|
||||
if (stream.GetSize() < header.offset_end)
|
||||
{
|
||||
NazaraError("Incomplete MD2 file");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Création du mesh
|
||||
// Le moteur ne supporte plus les animations image-clé, nous ne pouvons charger qu'en statique
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Chargement des skins
|
||||
if (header.num_skins > 0)
|
||||
{
|
||||
mesh->SetMaterialCount(header.num_skins);
|
||||
stream.SetCursorPos(header.offset_skins);
|
||||
if (stream.GetSize() < header.offset_end)
|
||||
{
|
||||
NzString baseDir = stream.GetDirectory();
|
||||
char skin[68];
|
||||
for (unsigned int i = 0; i < header.num_skins; ++i)
|
||||
NazaraError("Incomplete MD2 file");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Création du mesh
|
||||
// Le moteur ne supporte plus les animations image-clé, nous ne pouvons charger qu'en statique
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Chargement des skins
|
||||
if (header.num_skins > 0)
|
||||
{
|
||||
mesh->SetMaterialCount(header.num_skins);
|
||||
stream.SetCursorPos(header.offset_skins);
|
||||
{
|
||||
stream.Read(skin, 68*sizeof(char));
|
||||
mesh->SetMaterial(i, baseDir + skin);
|
||||
String baseDir = stream.GetDirectory();
|
||||
char skin[68];
|
||||
for (unsigned int i = 0; i < header.num_skins; ++i)
|
||||
{
|
||||
stream.Read(skin, 68*sizeof(char));
|
||||
mesh->SetMaterial(i, baseDir + skin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Chargement des submesh
|
||||
// Actuellement le loader ne charge qu'un submesh
|
||||
NzIndexBufferRef indexBuffer = NzIndexBuffer::New(false, header.num_tris*3, parameters.storage, nzBufferUsage_Static);
|
||||
/// Chargement des submesh
|
||||
// Actuellement le loader ne charge qu'un submesh
|
||||
IndexBufferRef indexBuffer = IndexBuffer::New(false, header.num_tris*3, parameters.storage, BufferUsage_Static);
|
||||
|
||||
/// Lecture des triangles
|
||||
std::vector<MD2_Triangle> triangles(header.num_tris);
|
||||
/// Lecture des triangles
|
||||
std::vector<MD2_Triangle> triangles(header.num_tris);
|
||||
|
||||
stream.SetCursorPos(header.offset_tris);
|
||||
stream.Read(&triangles[0], header.num_tris*sizeof(MD2_Triangle));
|
||||
stream.SetCursorPos(header.offset_tris);
|
||||
stream.Read(&triangles[0], header.num_tris*sizeof(MD2_Triangle));
|
||||
|
||||
NzBufferMapper<NzIndexBuffer> indexMapper(indexBuffer, nzBufferAccess_DiscardAndWrite);
|
||||
nzUInt16* index = reinterpret_cast<nzUInt16*>(indexMapper.GetPointer());
|
||||
BufferMapper<IndexBuffer> indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
|
||||
UInt16* index = reinterpret_cast<UInt16*>(indexMapper.GetPointer());
|
||||
|
||||
for (unsigned int i = 0; i < header.num_tris; ++i)
|
||||
{
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
SwapBytes(&triangles[i].vertices[0], sizeof(UInt16));
|
||||
SwapBytes(&triangles[i].texCoords[0], sizeof(UInt16));
|
||||
|
||||
SwapBytes(&triangles[i].vertices[1], sizeof(UInt16));
|
||||
SwapBytes(&triangles[i].texCoords[1], sizeof(UInt16));
|
||||
|
||||
SwapBytes(&triangles[i].vertices[2], sizeof(UInt16));
|
||||
SwapBytes(&triangles[i].texCoords[2], sizeof(UInt16));
|
||||
#endif
|
||||
|
||||
// On respécifie le triangle dans l'ordre attendu
|
||||
*index++ = triangles[i].vertices[0];
|
||||
*index++ = triangles[i].vertices[2];
|
||||
*index++ = triangles[i].vertices[1];
|
||||
}
|
||||
|
||||
indexMapper.Unmap();
|
||||
|
||||
if (parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
/// Lecture des coordonnées de texture
|
||||
std::vector<MD2_TexCoord> texCoords(header.num_st);
|
||||
|
||||
stream.SetCursorPos(header.offset_st);
|
||||
stream.Read(&texCoords[0], header.num_st*sizeof(MD2_TexCoord));
|
||||
|
||||
for (unsigned int i = 0; i < header.num_tris; ++i)
|
||||
{
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(&triangles[i].vertices[0], sizeof(nzUInt16));
|
||||
NzByteSwap(&triangles[i].texCoords[0], sizeof(nzUInt16));
|
||||
|
||||
NzByteSwap(&triangles[i].vertices[1], sizeof(nzUInt16));
|
||||
NzByteSwap(&triangles[i].texCoords[1], sizeof(nzUInt16));
|
||||
|
||||
NzByteSwap(&triangles[i].vertices[2], sizeof(nzUInt16));
|
||||
NzByteSwap(&triangles[i].texCoords[2], sizeof(nzUInt16));
|
||||
for (unsigned int i = 0; i < header.num_st; ++i)
|
||||
{
|
||||
SwapBytes(&texCoords[i].u, sizeof(Int16));
|
||||
SwapBytes(&texCoords[i].v, sizeof(Int16));
|
||||
}
|
||||
#endif
|
||||
|
||||
// On respécifie le triangle dans l'ordre attendu
|
||||
*index++ = triangles[i].vertices[0];
|
||||
*index++ = triangles[i].vertices[2];
|
||||
*index++ = triangles[i].vertices[1];
|
||||
}
|
||||
|
||||
indexMapper.Unmap();
|
||||
|
||||
if (parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
/// Lecture des coordonnées de texture
|
||||
std::vector<MD2_TexCoord> texCoords(header.num_st);
|
||||
|
||||
stream.SetCursorPos(header.offset_st);
|
||||
stream.Read(&texCoords[0], header.num_st*sizeof(MD2_TexCoord));
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
for (unsigned int i = 0; i < header.num_st; ++i)
|
||||
{
|
||||
NzByteSwap(&texCoords[i].u, sizeof(nzInt16));
|
||||
NzByteSwap(&texCoords[i].v, sizeof(nzInt16));
|
||||
}
|
||||
#endif
|
||||
|
||||
NzVertexBufferRef vertexBuffer = NzVertexBuffer::New(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent), header.num_vertices, parameters.storage, nzBufferUsage_Static);
|
||||
NzStaticMeshRef subMesh = NzStaticMesh::New(mesh);
|
||||
if (!subMesh->Create(vertexBuffer))
|
||||
{
|
||||
NazaraError("Failed to create SubMesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Chargement des vertices
|
||||
stream.SetCursorPos(header.offset_frames);
|
||||
|
||||
std::unique_ptr<MD2_Vertex[]> vertices(new MD2_Vertex[header.num_vertices]);
|
||||
NzVector3f scale, translate;
|
||||
stream.Read(scale, sizeof(NzVector3f));
|
||||
stream.Read(translate, sizeof(NzVector3f));
|
||||
stream.Read(nullptr, 16*sizeof(char)); // Nom de la frame, inutile ici
|
||||
stream.Read(vertices.get(), header.num_vertices*sizeof(MD2_Vertex));
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(&scale.x, sizeof(float));
|
||||
NzByteSwap(&scale.y, sizeof(float));
|
||||
NzByteSwap(&scale.z, sizeof(float));
|
||||
|
||||
NzByteSwap(&translate.x, sizeof(float));
|
||||
NzByteSwap(&translate.y, sizeof(float));
|
||||
NzByteSwap(&translate.z, sizeof(float));
|
||||
#endif
|
||||
|
||||
// Un personnage de taille moyenne fait ~50 unités de haut dans Quake 2
|
||||
// Avec Nazara, 1 unité = 1 mètre, nous devons donc adapter l'échelle
|
||||
NzVector3f s(parameters.scale/29.f); // 50/29 = 1.72 (Soit 1.72 mètre, proche de la taille moyenne d'un individu)
|
||||
scale *= s;
|
||||
translate *= s;
|
||||
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer, nzBufferAccess_DiscardAndWrite);
|
||||
NzMeshVertex* vertex = reinterpret_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
||||
|
||||
/// Chargement des coordonnées de texture
|
||||
const unsigned int indexFix[3] = {0, 2, 1}; // Pour respécifier les indices dans le bon ordre
|
||||
for (unsigned int i = 0; i < header.num_tris; ++i)
|
||||
{
|
||||
for (unsigned int j = 0; j < 3; ++j)
|
||||
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), header.num_vertices, parameters.storage, BufferUsage_Static);
|
||||
StaticMeshRef subMesh = StaticMesh::New(mesh);
|
||||
if (!subMesh->Create(vertexBuffer))
|
||||
{
|
||||
const unsigned int fixedIndex = indexFix[j];
|
||||
const MD2_TexCoord& texC = texCoords[triangles[i].texCoords[fixedIndex]];
|
||||
float u = static_cast<float>(texC.u) / header.skinwidth;
|
||||
float v = static_cast<float>(texC.v) / header.skinheight;
|
||||
|
||||
vertex[triangles[i].vertices[fixedIndex]].uv.Set(u, (parameters.flipUVs) ? 1.f - v : v);
|
||||
NazaraError("Failed to create SubMesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Chargement des vertices
|
||||
stream.SetCursorPos(header.offset_frames);
|
||||
|
||||
std::unique_ptr<MD2_Vertex[]> vertices(new MD2_Vertex[header.num_vertices]);
|
||||
Vector3f scale, translate;
|
||||
stream.Read(scale, sizeof(Vector3f));
|
||||
stream.Read(translate, sizeof(Vector3f));
|
||||
stream.Read(nullptr, 16*sizeof(char)); // Nom de la frame, inutile ici
|
||||
stream.Read(vertices.get(), header.num_vertices*sizeof(MD2_Vertex));
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
SwapBytes(&scale.x, sizeof(float));
|
||||
SwapBytes(&scale.y, sizeof(float));
|
||||
SwapBytes(&scale.z, sizeof(float));
|
||||
|
||||
SwapBytes(&translate.x, sizeof(float));
|
||||
SwapBytes(&translate.y, sizeof(float));
|
||||
SwapBytes(&translate.z, sizeof(float));
|
||||
#endif
|
||||
|
||||
// Un personnage de taille moyenne fait ~50 unités de haut dans Quake 2
|
||||
// Avec Nazara, 1 unité = 1 mètre, nous devons donc adapter l'échelle
|
||||
Vector3f s(parameters.scale/29.f); // 50/29 = 1.72 (Soit 1.72 mètre, proche de la taille moyenne d'un individu)
|
||||
scale *= s;
|
||||
translate *= s;
|
||||
|
||||
BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_DiscardAndWrite);
|
||||
MeshVertex* vertex = reinterpret_cast<MeshVertex*>(vertexMapper.GetPointer());
|
||||
|
||||
/// Chargement des coordonnées de texture
|
||||
const unsigned int indexFix[3] = {0, 2, 1}; // Pour respécifier les indices dans le bon ordre
|
||||
for (unsigned int i = 0; i < header.num_tris; ++i)
|
||||
{
|
||||
for (unsigned int j = 0; j < 3; ++j)
|
||||
{
|
||||
const unsigned int fixedIndex = indexFix[j];
|
||||
const MD2_TexCoord& texC = texCoords[triangles[i].texCoords[fixedIndex]];
|
||||
float u = static_cast<float>(texC.u) / header.skinwidth;
|
||||
float v = static_cast<float>(texC.v) / header.skinheight;
|
||||
|
||||
vertex[triangles[i].vertices[fixedIndex]].uv.Set(u, (parameters.flipUVs) ? 1.f - v : v);
|
||||
}
|
||||
}
|
||||
|
||||
/// Chargement des positions
|
||||
// Pour que le modèle soit correctement aligné, on génère un quaternion que nous appliquerons à chacune des vertices
|
||||
Quaternionf rotationQuat = EulerAnglesf(-90.f, 90.f, 0.f);
|
||||
|
||||
for (unsigned int v = 0; v < header.num_vertices; ++v)
|
||||
{
|
||||
const MD2_Vertex& vert = vertices[v];
|
||||
Vector3f position = rotationQuat * Vector3f(vert.x*scale.x + translate.x, vert.y*scale.y + translate.y, vert.z*scale.z + translate.z);
|
||||
|
||||
vertex->position = position;
|
||||
vertex->normal = rotationQuat * md2Normals[vert.n];
|
||||
|
||||
vertex++;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer);
|
||||
subMesh->SetMaterialIndex(0);
|
||||
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->GenerateTangents();
|
||||
|
||||
mesh->AddSubMesh(subMesh);
|
||||
|
||||
if (parameters.center)
|
||||
mesh->Recenter();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Chargement des positions
|
||||
// Pour que le modèle soit correctement aligné, on génère un quaternion que nous appliquerons à chacune des vertices
|
||||
NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 90.f, 0.f);
|
||||
|
||||
for (unsigned int v = 0; v < header.num_vertices; ++v)
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterMD2()
|
||||
{
|
||||
const MD2_Vertex& vert = vertices[v];
|
||||
NzVector3f position = rotationQuat * NzVector3f(vert.x*scale.x + translate.x, vert.y*scale.y + translate.y, vert.z*scale.z + translate.z);
|
||||
|
||||
vertex->position = position;
|
||||
vertex->normal = rotationQuat * md2Normals[vert.n];
|
||||
|
||||
vertex++;
|
||||
MeshLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer);
|
||||
subMesh->SetMaterialIndex(0);
|
||||
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->GenerateTangents();
|
||||
|
||||
mesh->AddSubMesh(subMesh);
|
||||
|
||||
if (parameters.center)
|
||||
mesh->Recenter();
|
||||
|
||||
return true;
|
||||
void UnregisterMD2()
|
||||
{
|
||||
MeshLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_MD2_Register()
|
||||
{
|
||||
NzMeshLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_MD2_Unregister()
|
||||
{
|
||||
NzMeshLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_MD2_Register();
|
||||
void NzLoaders_MD2_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterMD2();
|
||||
void UnregisterMD2();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_MD2_HPP
|
||||
|
||||
@@ -6,87 +6,93 @@
|
||||
#include <Nazara/Utility/Formats/MD5AnimParser.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
bool IsSupported(const NzString& extension)
|
||||
namespace
|
||||
{
|
||||
return (extension == "md5anim");
|
||||
}
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzAnimationParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
NzMD5AnimParser parser(stream);
|
||||
return parser.Check();
|
||||
}
|
||||
|
||||
bool Load(NzAnimation* animation, NzInputStream& stream, const NzAnimationParams& parameters)
|
||||
{
|
||||
///TODO: Utiliser les paramètres
|
||||
NzMD5AnimParser parser(stream);
|
||||
|
||||
if (!parser.Parse())
|
||||
bool IsSupported(const String& extension)
|
||||
{
|
||||
NazaraError("MD5Anim parser failed");
|
||||
return false;
|
||||
return (extension == "md5anim");
|
||||
}
|
||||
|
||||
const NzMD5AnimParser::Frame* frames = parser.GetFrames();
|
||||
unsigned int frameCount = parser.GetFrameCount();
|
||||
unsigned int frameRate = parser.GetFrameRate();
|
||||
const NzMD5AnimParser::Joint* joints = parser.GetJoints();
|
||||
unsigned int jointCount = parser.GetJointCount();
|
||||
|
||||
// À ce stade, nous sommes censés avoir assez d'informations pour créer l'animation
|
||||
animation->CreateSkeletal(frameCount, jointCount);
|
||||
|
||||
NzSequence sequence;
|
||||
sequence.firstFrame = 0;
|
||||
sequence.frameCount = frameCount;
|
||||
sequence.frameRate = frameRate;
|
||||
sequence.name = stream.GetPath().SubStringFrom(NAZARA_DIRECTORY_SEPARATOR, -1, true);
|
||||
|
||||
animation->AddSequence(sequence);
|
||||
|
||||
NzSequenceJoint* sequenceJoints = animation->GetSequenceJoints();
|
||||
|
||||
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
||||
NzQuaternionf rotationQuat = NzQuaternionf::RotationBetween(NzVector3f::UnitX(), NzVector3f::Forward()) *
|
||||
NzQuaternionf::RotationBetween(NzVector3f::UnitZ(), NzVector3f::Up());
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
Ternary Check(InputStream& stream, const AnimationParams& parameters)
|
||||
{
|
||||
int parent = joints[i].parent;
|
||||
for (unsigned int j = 0; j < frameCount; ++j)
|
||||
NazaraUnused(parameters);
|
||||
|
||||
MD5AnimParser parser(stream);
|
||||
return parser.Check();
|
||||
}
|
||||
|
||||
bool Load(Animation* animation, InputStream& stream, const AnimationParams& parameters)
|
||||
{
|
||||
///TODO: Utiliser les paramètres
|
||||
MD5AnimParser parser(stream);
|
||||
|
||||
if (!parser.Parse())
|
||||
{
|
||||
NzSequenceJoint& sequenceJoint = sequenceJoints[j*jointCount + i];
|
||||
|
||||
if (parent >= 0)
|
||||
{
|
||||
sequenceJoint.position = frames[j].joints[i].pos;
|
||||
sequenceJoint.rotation = frames[j].joints[i].orient;
|
||||
}
|
||||
else
|
||||
{
|
||||
sequenceJoint.position = rotationQuat * frames[j].joints[i].pos;
|
||||
sequenceJoint.rotation = rotationQuat * frames[j].joints[i].orient;
|
||||
}
|
||||
|
||||
sequenceJoint.scale.Set(1.f);
|
||||
NazaraError("MD5Anim parser failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const MD5AnimParser::Frame* frames = parser.GetFrames();
|
||||
unsigned int frameCount = parser.GetFrameCount();
|
||||
unsigned int frameRate = parser.GetFrameRate();
|
||||
const MD5AnimParser::Joint* joints = parser.GetJoints();
|
||||
unsigned int jointCount = parser.GetJointCount();
|
||||
|
||||
// À ce stade, nous sommes censés avoir assez d'informations pour créer l'animation
|
||||
animation->CreateSkeletal(frameCount, jointCount);
|
||||
|
||||
Sequence sequence;
|
||||
sequence.firstFrame = 0;
|
||||
sequence.frameCount = frameCount;
|
||||
sequence.frameRate = frameRate;
|
||||
sequence.name = stream.GetPath().SubStringFrom(NAZARA_DIRECTORY_SEPARATOR, -1, true);
|
||||
|
||||
animation->AddSequence(sequence);
|
||||
|
||||
SequenceJoint* sequenceJoints = animation->GetSequenceJoints();
|
||||
|
||||
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
||||
Quaternionf rotationQuat = Quaternionf::RotationBetween(Vector3f::UnitX(), Vector3f::Forward()) *
|
||||
Quaternionf::RotationBetween(Vector3f::UnitZ(), Vector3f::Up());
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
int parent = joints[i].parent;
|
||||
for (unsigned int j = 0; j < frameCount; ++j)
|
||||
{
|
||||
SequenceJoint& sequenceJoint = sequenceJoints[j*jointCount + i];
|
||||
|
||||
if (parent >= 0)
|
||||
{
|
||||
sequenceJoint.position = frames[j].joints[i].pos;
|
||||
sequenceJoint.rotation = frames[j].joints[i].orient;
|
||||
}
|
||||
else
|
||||
{
|
||||
sequenceJoint.position = rotationQuat * frames[j].joints[i].pos;
|
||||
sequenceJoint.rotation = rotationQuat * frames[j].joints[i].orient;
|
||||
}
|
||||
|
||||
sequenceJoint.scale.Set(1.f);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterMD5Anim()
|
||||
{
|
||||
AnimationLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
return true;
|
||||
void UnregisterMD5Anim()
|
||||
{
|
||||
AnimationLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_MD5Anim_Register()
|
||||
{
|
||||
NzAnimationLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_MD5Anim_Unregister()
|
||||
{
|
||||
NzAnimationLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_MD5Anim_Register();
|
||||
void NzLoaders_MD5Anim_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterMD5Anim();
|
||||
void UnregisterMD5Anim();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_MD5ANIM_HPP
|
||||
|
||||
@@ -13,509 +13,512 @@
|
||||
#include <limits>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzMD5AnimParser::NzMD5AnimParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_keepLastLine(false),
|
||||
m_frameIndex(0),
|
||||
m_frameRate(0),
|
||||
m_lineCount(0),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
namespace Nz
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | nzStreamOption_Text);
|
||||
}
|
||||
|
||||
NzMD5AnimParser::~NzMD5AnimParser()
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
nzTernary NzMD5AnimParser::Check()
|
||||
{
|
||||
if (Advance(false))
|
||||
MD5AnimParser::MD5AnimParser(InputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_keepLastLine(false),
|
||||
m_frameIndex(0),
|
||||
m_frameRate(0),
|
||||
m_lineCount(0),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
{
|
||||
unsigned int version;
|
||||
if (std::sscanf(&m_currentLine[0], " MD5Version %u", &version) == 1)
|
||||
{
|
||||
if (version == 10)
|
||||
return nzTernary_True;
|
||||
}
|
||||
if ((m_streamFlags & StreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | StreamOption_Text);
|
||||
}
|
||||
|
||||
return nzTernary_False;
|
||||
}
|
||||
|
||||
unsigned int NzMD5AnimParser::GetAnimatedComponentCount() const
|
||||
{
|
||||
return m_animatedComponents.size();
|
||||
}
|
||||
|
||||
const NzMD5AnimParser::Frame* NzMD5AnimParser::GetFrames() const
|
||||
{
|
||||
return m_frames.data();
|
||||
}
|
||||
|
||||
unsigned int NzMD5AnimParser::GetFrameCount() const
|
||||
{
|
||||
return m_frames.size();
|
||||
}
|
||||
|
||||
unsigned int NzMD5AnimParser::GetFrameRate() const
|
||||
{
|
||||
return m_frameRate;
|
||||
}
|
||||
|
||||
const NzMD5AnimParser::Joint* NzMD5AnimParser::GetJoints() const
|
||||
{
|
||||
return m_joints.data();
|
||||
}
|
||||
|
||||
unsigned int NzMD5AnimParser::GetJointCount() const
|
||||
{
|
||||
return m_joints.size();
|
||||
}
|
||||
|
||||
bool NzMD5AnimParser::Parse()
|
||||
{
|
||||
while (Advance(false))
|
||||
MD5AnimParser::~MD5AnimParser()
|
||||
{
|
||||
switch (m_currentLine[0])
|
||||
if ((m_streamFlags & StreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
Ternary MD5AnimParser::Check()
|
||||
{
|
||||
if (Advance(false))
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
case 'M': // MD5Version
|
||||
if (m_currentLine.GetWord(0) != "MD5Version")
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'b': // baseframe/bounds
|
||||
if (m_currentLine.StartsWith("baseframe {"))
|
||||
{
|
||||
if (!ParseBaseframe())
|
||||
{
|
||||
Error("Failed to parse baseframe");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (m_currentLine.StartsWith("bounds {"))
|
||||
{
|
||||
if (!ParseBounds())
|
||||
{
|
||||
Error("Failed to parse bounds");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
case 'c': // commandline
|
||||
if (m_currentLine.GetWord(0) != "commandline")
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'f':
|
||||
unsigned int version;
|
||||
if (std::sscanf(&m_currentLine[0], " MD5Version %u", &version) == 1)
|
||||
{
|
||||
unsigned int index;
|
||||
if (std::sscanf(&m_currentLine[0], "frame %u {", &index) == 1)
|
||||
{
|
||||
if (m_frameIndex != index)
|
||||
{
|
||||
Error("Unexpected frame index (expected " + NzString::Number(m_frameIndex) + ", got " + NzString::Number(index) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ParseFrame())
|
||||
{
|
||||
Error("Failed to parse frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_frameIndex++;
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "frameRate %u", &m_frameRate) != 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
if (version == 10)
|
||||
return Ternary_True;
|
||||
}
|
||||
|
||||
case 'h': // hierarchy
|
||||
if (m_currentLine.StartsWith("hierarchy {"))
|
||||
{
|
||||
if (!ParseHierarchy())
|
||||
{
|
||||
Error("Failed to parse hierarchy");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 'n': // num[Frames/Joints]
|
||||
{
|
||||
unsigned int count;
|
||||
if (std::sscanf(&m_currentLine[0], "numAnimatedComponents %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_animatedComponents.empty())
|
||||
Warning("Animated components count is already defined");
|
||||
#endif
|
||||
|
||||
m_animatedComponents.resize(count);
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numFrames %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_frames.empty())
|
||||
Warning("Frame count is already defined");
|
||||
#endif
|
||||
|
||||
m_frames.resize(count);
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numJoints %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_joints.empty())
|
||||
Warning("Joint count is already defined");
|
||||
#endif
|
||||
|
||||
m_joints.resize(count);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return Ternary_False;
|
||||
}
|
||||
|
||||
unsigned int frameCount = m_frames.size();
|
||||
if (frameCount == 0)
|
||||
unsigned int MD5AnimParser::GetAnimatedComponentCount() const
|
||||
{
|
||||
NazaraError("Frame count is invalid or missing");
|
||||
return false;
|
||||
return m_animatedComponents.size();
|
||||
}
|
||||
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
const MD5AnimParser::Frame* MD5AnimParser::GetFrames() const
|
||||
{
|
||||
NazaraError("Joint count is invalid or missing");
|
||||
return false;
|
||||
return m_frames.data();
|
||||
}
|
||||
|
||||
if (m_frameIndex != frameCount)
|
||||
unsigned int MD5AnimParser::GetFrameCount() const
|
||||
{
|
||||
NazaraError("Missing frame infos: [" + NzString::Number(m_frameIndex) + ',' + NzString::Number(frameCount) + ']');
|
||||
return false;
|
||||
return m_frames.size();
|
||||
}
|
||||
|
||||
if (m_frameRate == 0)
|
||||
unsigned int MD5AnimParser::GetFrameRate() const
|
||||
{
|
||||
NazaraWarning("Framerate is either invalid or missing, assuming a default value of 24");
|
||||
m_frameRate = 24;
|
||||
return m_frameRate;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzMD5AnimParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
const MD5AnimParser::Joint* MD5AnimParser::GetJoints() const
|
||||
{
|
||||
do
|
||||
return m_joints.data();
|
||||
}
|
||||
|
||||
unsigned int MD5AnimParser::GetJointCount() const
|
||||
{
|
||||
return m_joints.size();
|
||||
}
|
||||
|
||||
bool MD5AnimParser::Parse()
|
||||
{
|
||||
while (Advance(false))
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
switch (m_currentLine[0])
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete MD5 file");
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
case 'M': // MD5Version
|
||||
if (m_currentLine.GetWord(0) != "MD5Version")
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'b': // baseframe/bounds
|
||||
if (m_currentLine.StartsWith("baseframe {"))
|
||||
{
|
||||
if (!ParseBaseframe())
|
||||
{
|
||||
Error("Failed to parse baseframe");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (m_currentLine.StartsWith("bounds {"))
|
||||
{
|
||||
if (!ParseBounds())
|
||||
{
|
||||
Error("Failed to parse bounds");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
case 'c': // commandline
|
||||
if (m_currentLine.GetWord(0) != "commandline")
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'f':
|
||||
{
|
||||
unsigned int index;
|
||||
if (std::sscanf(&m_currentLine[0], "frame %u {", &index) == 1)
|
||||
{
|
||||
if (m_frameIndex != index)
|
||||
{
|
||||
Error("Unexpected frame index (expected " + String::Number(m_frameIndex) + ", got " + String::Number(index) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ParseFrame())
|
||||
{
|
||||
Error("Failed to parse frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_frameIndex++;
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "frameRate %u", &m_frameRate) != 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'h': // hierarchy
|
||||
if (m_currentLine.StartsWith("hierarchy {"))
|
||||
{
|
||||
if (!ParseHierarchy())
|
||||
{
|
||||
Error("Failed to parse hierarchy");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 'n': // num[Frames/Joints]
|
||||
{
|
||||
unsigned int count;
|
||||
if (std::sscanf(&m_currentLine[0], "numAnimatedComponents %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_animatedComponents.empty())
|
||||
Warning("Animated components count is already defined");
|
||||
#endif
|
||||
|
||||
m_animatedComponents.resize(count);
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numFrames %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_frames.empty())
|
||||
Warning("Frame count is already defined");
|
||||
#endif
|
||||
|
||||
m_frames.resize(count);
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numJoints %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_joints.empty())
|
||||
Warning("Joint count is already defined");
|
||||
#endif
|
||||
|
||||
m_joints.resize(count);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int frameCount = m_frames.size();
|
||||
if (frameCount == 0)
|
||||
{
|
||||
NazaraError("Frame count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
{
|
||||
NazaraError("Joint count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_frameIndex != frameCount)
|
||||
{
|
||||
NazaraError("Missing frame infos: [" + String::Number(m_frameIndex) + ',' + String::Number(frameCount) + ']');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_frameRate == 0)
|
||||
{
|
||||
NazaraWarning("Framerate is either invalid or missing, assuming a default value of 24");
|
||||
m_frameRate = 24;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MD5AnimParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete MD5 file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubStringTo("//"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MD5AnimParser::Error(const String& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + String::Number(m_lineCount));
|
||||
}
|
||||
|
||||
bool MD5AnimParser::ParseBaseframe()
|
||||
{
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
{
|
||||
Error("Joint count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
if (std::sscanf(&m_currentLine[0], "( %f %f %f ) ( %f %f %f )", &m_joints[i].bindPos.x, &m_joints[i].bindPos.y, &m_joints[i].bindPos.z,
|
||||
&m_joints[i].bindOrient.x, &m_joints[i].bindOrient.y, &m_joints[i].bindOrient.z) != 6)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubStringTo("//"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzMD5AnimParser::Error(const NzString& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
bool NzMD5AnimParser::ParseBaseframe()
|
||||
{
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
{
|
||||
Error("Joint count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
if (std::sscanf(&m_currentLine[0], "( %f %f %f ) ( %f %f %f )", &m_joints[i].bindPos.x, &m_joints[i].bindPos.y, &m_joints[i].bindPos.z,
|
||||
&m_joints[i].bindOrient.x, &m_joints[i].bindOrient.y, &m_joints[i].bindOrient.z) != 6)
|
||||
if (m_currentLine != '}')
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Bounds braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
if (m_currentLine != '}')
|
||||
bool MD5AnimParser::ParseBounds()
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Bounds braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzMD5AnimParser::ParseBounds()
|
||||
{
|
||||
unsigned int frameCount = m_frames.size();
|
||||
if (frameCount == 0)
|
||||
{
|
||||
Error("Frame count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < frameCount; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
NzVector3f min, max;
|
||||
if (std::sscanf(&m_currentLine[0], "( %f %f %f ) ( %f %f %f )", &min.x, &min.y, &min.z, &max.x, &max.y, &max.z) != 6)
|
||||
unsigned int frameCount = m_frames.size();
|
||||
if (frameCount == 0)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
Error("Frame count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_frames[i].bounds.Set(min, max);
|
||||
}
|
||||
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
if (m_currentLine != '}')
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Bounds braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzMD5AnimParser::ParseFrame()
|
||||
{
|
||||
unsigned int animatedComponentsCount = m_animatedComponents.size();
|
||||
if (animatedComponentsCount == 0)
|
||||
{
|
||||
Error("Animated components count is missing or invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
{
|
||||
Error("Joint count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
NzString line;
|
||||
|
||||
unsigned int count = 0;
|
||||
do
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
unsigned int index = 0;
|
||||
unsigned int size = m_currentLine.GetSize();
|
||||
do
|
||||
for (unsigned int i = 0; i < frameCount; ++i)
|
||||
{
|
||||
float f;
|
||||
int read;
|
||||
if (std::sscanf(&m_currentLine[index], "%f%n", &f, &read) != 1)
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Vector3f min, max;
|
||||
if (std::sscanf(&m_currentLine[0], "( %f %f %f ) ( %f %f %f )", &min.x, &min.y, &min.z, &max.x, &max.y, &max.z) != 6)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
index += read;
|
||||
|
||||
m_animatedComponents[count] = f;
|
||||
|
||||
count++;
|
||||
m_frames[i].bounds.Set(min, max);
|
||||
}
|
||||
while (index < size);
|
||||
}
|
||||
while (count < animatedComponentsCount);
|
||||
|
||||
m_frames[m_frameIndex].joints.resize(jointCount);
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
NzQuaternionf jointOrient = m_joints[i].bindOrient;
|
||||
NzVector3f jointPos = m_joints[i].bindPos;
|
||||
unsigned int j = 0;
|
||||
|
||||
if (m_joints[i].flags & 1) // Px
|
||||
jointPos.x = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 2) // Py
|
||||
jointPos.y = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 4) // Pz
|
||||
jointPos.z = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 8) // Qx
|
||||
jointOrient.x = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 16) // Qy
|
||||
jointOrient.y = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 32) // Qz
|
||||
jointOrient.z = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
jointOrient.ComputeW();
|
||||
|
||||
m_frames[m_frameIndex].joints[i].orient = jointOrient;
|
||||
m_frames[m_frameIndex].joints[i].pos = jointPos;
|
||||
}
|
||||
|
||||
if (!Advance(false))
|
||||
return true;
|
||||
|
||||
if (m_currentLine != '}')
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Hierarchy braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzMD5AnimParser::ParseHierarchy()
|
||||
{
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
{
|
||||
Error("Joint count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
unsigned int pos = m_currentLine.Find(' ');
|
||||
if (pos == NzString::npos)
|
||||
if (m_currentLine != '}')
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Bounds braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MD5AnimParser::ParseFrame()
|
||||
{
|
||||
unsigned int animatedComponentsCount = m_animatedComponents.size();
|
||||
if (animatedComponentsCount == 0)
|
||||
{
|
||||
Error("Animated components count is missing or invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos >= 64)
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
{
|
||||
NazaraError("Joint name is too long (>= 64 characters)");
|
||||
Error("Joint count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
char name[64];
|
||||
if (std::sscanf(&m_currentLine[0], "%63s %d %u %u", &name[0], &m_joints[i].parent, &m_joints[i].flags, &m_joints[i].index) != 4)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
String line;
|
||||
|
||||
m_joints[i].name = name;
|
||||
m_joints[i].name.Trim('"');
|
||||
|
||||
int parent = m_joints[i].parent;
|
||||
if (parent >= 0)
|
||||
unsigned int count = 0;
|
||||
do
|
||||
{
|
||||
if (static_cast<unsigned int>(parent) >= jointCount)
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
unsigned int index = 0;
|
||||
unsigned int size = m_currentLine.GetSize();
|
||||
do
|
||||
{
|
||||
Error("Joint's parent is out of bounds (" + NzString::Number(parent) + " >= " + NzString::Number(jointCount) + ')');
|
||||
float f;
|
||||
int read;
|
||||
if (std::sscanf(&m_currentLine[index], "%f%n", &f, &read) != 1)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
index += read;
|
||||
|
||||
m_animatedComponents[count] = f;
|
||||
|
||||
count++;
|
||||
}
|
||||
while (index < size);
|
||||
}
|
||||
while (count < animatedComponentsCount);
|
||||
|
||||
m_frames[m_frameIndex].joints.resize(jointCount);
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
Quaternionf jointOrient = m_joints[i].bindOrient;
|
||||
Vector3f jointPos = m_joints[i].bindPos;
|
||||
unsigned int j = 0;
|
||||
|
||||
if (m_joints[i].flags & 1) // Px
|
||||
jointPos.x = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 2) // Py
|
||||
jointPos.y = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 4) // Pz
|
||||
jointPos.z = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 8) // Qx
|
||||
jointOrient.x = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 16) // Qy
|
||||
jointOrient.y = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
if (m_joints[i].flags & 32) // Qz
|
||||
jointOrient.z = m_animatedComponents[m_joints[i].index + j++];
|
||||
|
||||
jointOrient.ComputeW();
|
||||
|
||||
m_frames[m_frameIndex].joints[i].orient = jointOrient;
|
||||
m_frames[m_frameIndex].joints[i].pos = jointPos;
|
||||
}
|
||||
|
||||
if (!Advance(false))
|
||||
return true;
|
||||
|
||||
if (m_currentLine != '}')
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Hierarchy braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MD5AnimParser::ParseHierarchy()
|
||||
{
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
{
|
||||
Error("Joint count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
unsigned int pos = m_currentLine.Find(' ');
|
||||
if (pos == String::npos)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos >= 64)
|
||||
{
|
||||
NazaraError("Joint name is too long (>= 64 characters)");
|
||||
return false;
|
||||
}
|
||||
|
||||
char name[64];
|
||||
if (std::sscanf(&m_currentLine[0], "%63s %d %u %u", &name[0], &m_joints[i].parent, &m_joints[i].flags, &m_joints[i].index) != 4)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_joints[i].name = name;
|
||||
m_joints[i].name.Trim('"');
|
||||
|
||||
int parent = m_joints[i].parent;
|
||||
if (parent >= 0)
|
||||
{
|
||||
if (static_cast<unsigned int>(parent) >= jointCount)
|
||||
{
|
||||
Error("Joint's parent is out of bounds (" + String::Number(parent) + " >= " + String::Number(jointCount) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
if (m_currentLine != '}')
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Hierarchy braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
if (m_currentLine != '}')
|
||||
void MD5AnimParser::Warning(const String& message)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Hierarchy braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
NazaraWarning(message + " at line #" + String::Number(m_lineCount));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
void MD5AnimParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
String message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
void NzMD5AnimParser::Warning(const NzString& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzMD5AnimParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
NzString message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,293 +11,299 @@
|
||||
#include <memory>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
bool IsSupported(const NzString& extension)
|
||||
namespace
|
||||
{
|
||||
return (extension == "md5mesh");
|
||||
}
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzMeshParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
NzMD5MeshParser parser(stream);
|
||||
return parser.Check();
|
||||
}
|
||||
|
||||
bool Load(NzMesh* mesh, NzInputStream& stream, const NzMeshParams& parameters)
|
||||
{
|
||||
NzMD5MeshParser parser(stream);
|
||||
if (!parser.Parse())
|
||||
bool IsSupported(const String& extension)
|
||||
{
|
||||
NazaraError("MD5Mesh parser failed");
|
||||
return false;
|
||||
return (extension == "md5mesh");
|
||||
}
|
||||
|
||||
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
||||
NzQuaternionf rotationQuat = NzQuaternionf::RotationBetween(NzVector3f::UnitX(), NzVector3f::Forward()) *
|
||||
NzQuaternionf::RotationBetween(NzVector3f::UnitZ(), NzVector3f::Up());
|
||||
|
||||
NzString baseDir = stream.GetDirectory();
|
||||
|
||||
// Le hellknight de Doom 3 fait ~120 unités, et il est dit qu'il fait trois mètres
|
||||
// Nous réduisons donc la taille générale des fichiers MD5 de 1/40
|
||||
NzVector3f scale(parameters.scale/40.f);
|
||||
|
||||
const NzMD5MeshParser::Joint* joints = parser.GetJoints();
|
||||
const NzMD5MeshParser::Mesh* meshes = parser.GetMeshes();
|
||||
unsigned int jointCount = parser.GetJointCount();
|
||||
unsigned int meshCount = parser.GetMeshCount();
|
||||
|
||||
if (parameters.animated)
|
||||
Ternary Check(InputStream& stream, const MeshParams& parameters)
|
||||
{
|
||||
mesh->CreateSkeletal(jointCount);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
NzSkeleton* skeleton = mesh->GetSkeleton();
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
NzJoint* joint = skeleton->GetJoint(i);
|
||||
|
||||
int parent = joints[i].parent;
|
||||
if (parent >= 0)
|
||||
joint->SetParent(skeleton->GetJoint(parent));
|
||||
|
||||
joint->SetName(joints[i].name);
|
||||
|
||||
NzMatrix4f bindMatrix;
|
||||
|
||||
if (parent >= 0)
|
||||
bindMatrix.MakeTransform(joints[i].bindPos, joints[i].bindOrient);
|
||||
else
|
||||
bindMatrix.MakeTransform(rotationQuat * joints[i].bindPos, rotationQuat * joints[i].bindOrient);
|
||||
|
||||
joint->SetInverseBindMatrix(bindMatrix.InverseAffine());
|
||||
}
|
||||
|
||||
mesh->SetMaterialCount(meshCount);
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
{
|
||||
const NzMD5MeshParser::Mesh& md5Mesh = meshes[i];
|
||||
|
||||
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
||||
unsigned int vertexCount = md5Mesh.vertices.size();
|
||||
|
||||
bool largeIndices = (vertexCount > std::numeric_limits<nzUInt16>::max());
|
||||
|
||||
NzIndexBufferRef indexBuffer = NzIndexBuffer::New(largeIndices, indexCount, parameters.storage);
|
||||
NzVertexBufferRef vertexBuffer = NzVertexBuffer::New(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent_Skinning), vertexCount, parameters.storage, nzBufferUsage_Static);
|
||||
|
||||
// Index buffer
|
||||
NzIndexMapper indexMapper(indexBuffer, nzBufferAccess_DiscardAndWrite);
|
||||
|
||||
// Le format définit un set de triangles nous permettant de retrouver facilement les indices
|
||||
// Cependant les sommets des triangles ne sont pas spécifiés dans le même ordre que ceux du moteur
|
||||
// (On parle ici de winding)
|
||||
unsigned int index = 0;
|
||||
for (const NzMD5MeshParser::Triangle& triangle : md5Mesh.triangles)
|
||||
{
|
||||
// On les respécifie dans le bon ordre (inversion du winding)
|
||||
indexMapper.Set(index++, triangle.x);
|
||||
indexMapper.Set(index++, triangle.z);
|
||||
indexMapper.Set(index++, triangle.y);
|
||||
}
|
||||
|
||||
indexMapper.Unmap();
|
||||
|
||||
if (parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
// Vertex buffer
|
||||
struct Weight
|
||||
{
|
||||
float bias;
|
||||
unsigned int jointIndex;
|
||||
};
|
||||
|
||||
std::vector<Weight> tempWeights;
|
||||
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer, nzBufferAccess_WriteOnly);
|
||||
NzSkeletalMeshVertex* vertices = static_cast<NzSkeletalMeshVertex*>(vertexMapper.GetPointer());
|
||||
for (const NzMD5MeshParser::Vertex& vertex : md5Mesh.vertices)
|
||||
{
|
||||
// Skinning MD5 (Formule d'Id Tech)
|
||||
NzVector3f finalPos(NzVector3f::Zero());
|
||||
|
||||
// On stocke tous les poids dans le tableau temporaire en même temps qu'on calcule la position finale du sommet.
|
||||
tempWeights.resize(vertex.weightCount);
|
||||
for (unsigned int j = 0; j < vertex.weightCount; ++j)
|
||||
{
|
||||
const NzMD5MeshParser::Weight& weight = md5Mesh.weights[vertex.startWeight + j];
|
||||
const NzMD5MeshParser::Joint& joint = joints[weight.joint];
|
||||
|
||||
finalPos += (joint.bindPos + joint.bindOrient*weight.pos) * weight.bias;
|
||||
|
||||
// Avant d'ajouter les poids, il faut s'assurer qu'il n'y en ait pas plus que le maximum supporté
|
||||
// et dans le cas contraire, garder les poids les plus importants et les renormaliser
|
||||
tempWeights[j] = {weight.bias, weight.joint};
|
||||
}
|
||||
|
||||
// Avons nous plus de poids que le moteur ne peut en supporter ?
|
||||
unsigned int weightCount = vertex.weightCount;
|
||||
if (weightCount > NAZARA_UTILITY_SKINNING_MAX_WEIGHTS)
|
||||
{
|
||||
// Pour augmenter la qualité du skinning tout en ne gardant que X poids, on ne garde que les poids
|
||||
// les plus importants, ayant le plus d'impact sur le sommet final
|
||||
std::sort(tempWeights.begin(), tempWeights.end(), [] (const Weight& a, const Weight& b) -> bool {
|
||||
return a.bias > b.bias;
|
||||
});
|
||||
|
||||
// Sans oublier bien sûr de renormaliser les poids (que leur somme soit 1)
|
||||
float weightSum = 0.f;
|
||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
||||
weightSum += tempWeights[j].bias;
|
||||
|
||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
||||
tempWeights[j].bias /= weightSum;
|
||||
|
||||
weightCount = NAZARA_UTILITY_SKINNING_MAX_WEIGHTS;
|
||||
}
|
||||
|
||||
vertices->weightCount = weightCount;
|
||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
||||
{
|
||||
if (j < weightCount)
|
||||
{
|
||||
// On donne une valeur aux poids présents
|
||||
vertices->weights[j] = tempWeights[j].bias;
|
||||
vertices->jointIndexes[j] = tempWeights[j].jointIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Et un poids de 0 sur le joint 0 pour les autres (nécessaire pour le GPU Skinning)
|
||||
// La raison est que le GPU ne tiendra pas compte du nombre de poids pour des raisons de performances.
|
||||
vertices->weights[j] = 0.f;
|
||||
vertices->jointIndexes[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vertices->position = finalPos;
|
||||
vertices->uv.Set(vertex.uv.x, (parameters.flipUVs) ? 1.f - vertex.uv.y : vertex.uv.y); // Inversion des UV si demandé
|
||||
vertices++;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
// Material
|
||||
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
|
||||
|
||||
// Submesh
|
||||
NzSkeletalMeshRef subMesh = NzSkeletalMesh::New(mesh);
|
||||
subMesh->Create(vertexBuffer);
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer);
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
subMesh->SetMaterialIndex(i);
|
||||
subMesh->SetPrimitiveMode(nzPrimitiveMode_TriangleList);
|
||||
|
||||
mesh->AddSubMesh(subMesh);
|
||||
|
||||
// Animation
|
||||
// Il est peut-être éventuellement possible que la probabilité que l'animation ait le même nom soit non-nulle.
|
||||
NzString path = stream.GetPath();
|
||||
if (!path.IsEmpty())
|
||||
{
|
||||
path.Replace(".md5mesh", ".md5anim", -8, NzString::CaseInsensitive);
|
||||
if (NzFile::Exists(path))
|
||||
mesh->SetAnimation(path);
|
||||
}
|
||||
}
|
||||
MD5MeshParser parser(stream);
|
||||
return parser.Check();
|
||||
}
|
||||
else
|
||||
|
||||
bool Load(Mesh* mesh, InputStream& stream, const MeshParams& parameters)
|
||||
{
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
MD5MeshParser parser(stream);
|
||||
if (!parser.Parse())
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
NazaraError("MD5Mesh parser failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
mesh->SetMaterialCount(meshCount);
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
||||
Quaternionf rotationQuat = Quaternionf::RotationBetween(Vector3f::UnitX(), Vector3f::Forward()) *
|
||||
Quaternionf::RotationBetween(Vector3f::UnitZ(), Vector3f::Up());
|
||||
|
||||
String baseDir = stream.GetDirectory();
|
||||
|
||||
// Le hellknight de Doom 3 fait ~120 unités, et il est dit qu'il fait trois mètres
|
||||
// Nous réduisons donc la taille générale des fichiers MD5 de 1/40
|
||||
Vector3f scale(parameters.scale/40.f);
|
||||
|
||||
const MD5MeshParser::Joint* joints = parser.GetJoints();
|
||||
const MD5MeshParser::Mesh* meshes = parser.GetMeshes();
|
||||
unsigned int jointCount = parser.GetJointCount();
|
||||
unsigned int meshCount = parser.GetMeshCount();
|
||||
|
||||
if (parameters.animated)
|
||||
{
|
||||
const NzMD5MeshParser::Mesh& md5Mesh = meshes[i];
|
||||
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
||||
unsigned int vertexCount = md5Mesh.vertices.size();
|
||||
mesh->CreateSkeletal(jointCount);
|
||||
|
||||
// Index buffer
|
||||
bool largeIndices = (vertexCount > std::numeric_limits<nzUInt16>::max());
|
||||
|
||||
NzIndexBufferRef indexBuffer = NzIndexBuffer::New(largeIndices, indexCount, parameters.storage);
|
||||
|
||||
NzIndexMapper indexMapper(indexBuffer, nzBufferAccess_DiscardAndWrite);
|
||||
NzIndexIterator index = indexMapper.begin();
|
||||
|
||||
for (const NzMD5MeshParser::Triangle& triangle : md5Mesh.triangles)
|
||||
Skeleton* skeleton = mesh->GetSkeleton();
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
// On les respécifie dans le bon ordre
|
||||
*index++ = triangle.x;
|
||||
*index++ = triangle.z;
|
||||
*index++ = triangle.y;
|
||||
Joint* joint = skeleton->GetJoint(i);
|
||||
|
||||
int parent = joints[i].parent;
|
||||
if (parent >= 0)
|
||||
joint->SetParent(skeleton->GetJoint(parent));
|
||||
|
||||
joint->SetName(joints[i].name);
|
||||
|
||||
Matrix4f bindMatrix;
|
||||
|
||||
if (parent >= 0)
|
||||
bindMatrix.MakeTransform(joints[i].bindPos, joints[i].bindOrient);
|
||||
else
|
||||
bindMatrix.MakeTransform(rotationQuat * joints[i].bindPos, rotationQuat * joints[i].bindOrient);
|
||||
|
||||
joint->SetInverseBindMatrix(bindMatrix.InverseAffine());
|
||||
}
|
||||
indexMapper.Unmap();
|
||||
|
||||
// Vertex buffer
|
||||
NzVertexBufferRef vertexBuffer = NzVertexBuffer::New(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage);
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer, nzBufferAccess_WriteOnly);
|
||||
|
||||
NzMeshVertex* vertex = reinterpret_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
||||
for (const NzMD5MeshParser::Vertex& md5Vertex : md5Mesh.vertices)
|
||||
mesh->SetMaterialCount(meshCount);
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
{
|
||||
// Skinning MD5 (Formule d'Id Tech)
|
||||
NzVector3f finalPos(NzVector3f::Zero());
|
||||
for (unsigned int j = 0; j < md5Vertex.weightCount; ++j)
|
||||
{
|
||||
const NzMD5MeshParser::Weight& weight = md5Mesh.weights[md5Vertex.startWeight + j];
|
||||
const NzMD5MeshParser::Joint& joint = joints[weight.joint];
|
||||
const MD5MeshParser::Mesh& md5Mesh = meshes[i];
|
||||
|
||||
finalPos += (joint.bindPos + joint.bindOrient*weight.pos) * weight.bias;
|
||||
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
||||
unsigned int vertexCount = md5Mesh.vertices.size();
|
||||
|
||||
bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max());
|
||||
|
||||
IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage);
|
||||
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent_Skinning), vertexCount, parameters.storage, BufferUsage_Static);
|
||||
|
||||
// Index buffer
|
||||
IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
|
||||
|
||||
// Le format définit un set de triangles nous permettant de retrouver facilement les indices
|
||||
// Cependant les sommets des triangles ne sont pas spécifiés dans le même ordre que ceux du moteur
|
||||
// (On parle ici de winding)
|
||||
unsigned int index = 0;
|
||||
for (const MD5MeshParser::Triangle& triangle : md5Mesh.triangles)
|
||||
{
|
||||
// On les respécifie dans le bon ordre (inversion du winding)
|
||||
indexMapper.Set(index++, triangle.x);
|
||||
indexMapper.Set(index++, triangle.z);
|
||||
indexMapper.Set(index++, triangle.y);
|
||||
}
|
||||
|
||||
// On retourne le modèle dans le bon sens
|
||||
vertex->position = scale * (rotationQuat * finalPos);
|
||||
vertex->uv.Set(md5Vertex.uv.x, (parameters.flipUVs) ? 1.f - md5Vertex.uv.y : md5Vertex.uv.y); // Inversion des UV si demandé
|
||||
vertex++;
|
||||
indexMapper.Unmap();
|
||||
|
||||
if (parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
// Vertex buffer
|
||||
struct Weight
|
||||
{
|
||||
float bias;
|
||||
unsigned int jointIndex;
|
||||
};
|
||||
|
||||
std::vector<Weight> tempWeights;
|
||||
|
||||
BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);
|
||||
SkeletalMeshVertex* vertices = static_cast<SkeletalMeshVertex*>(vertexMapper.GetPointer());
|
||||
for (const MD5MeshParser::Vertex& vertex : md5Mesh.vertices)
|
||||
{
|
||||
// Skinning MD5 (Formule d'Id Tech)
|
||||
Vector3f finalPos(Vector3f::Zero());
|
||||
|
||||
// On stocke tous les poids dans le tableau temporaire en même temps qu'on calcule la position finale du sommet.
|
||||
tempWeights.resize(vertex.weightCount);
|
||||
for (unsigned int j = 0; j < vertex.weightCount; ++j)
|
||||
{
|
||||
const MD5MeshParser::Weight& weight = md5Mesh.weights[vertex.startWeight + j];
|
||||
const MD5MeshParser::Joint& joint = joints[weight.joint];
|
||||
|
||||
finalPos += (joint.bindPos + joint.bindOrient*weight.pos) * weight.bias;
|
||||
|
||||
// Avant d'ajouter les poids, il faut s'assurer qu'il n'y en ait pas plus que le maximum supporté
|
||||
// et dans le cas contraire, garder les poids les plus importants et les renormaliser
|
||||
tempWeights[j] = {weight.bias, weight.joint};
|
||||
}
|
||||
|
||||
// Avons nous plus de poids que le moteur ne peut en supporter ?
|
||||
unsigned int weightCount = vertex.weightCount;
|
||||
if (weightCount > NAZARA_UTILITY_SKINNING_MAX_WEIGHTS)
|
||||
{
|
||||
// Pour augmenter la qualité du skinning tout en ne gardant que X poids, on ne garde que les poids
|
||||
// les plus importants, ayant le plus d'impact sur le sommet final
|
||||
std::sort(tempWeights.begin(), tempWeights.end(), [] (const Weight& a, const Weight& b) -> bool {
|
||||
return a.bias > b.bias;
|
||||
});
|
||||
|
||||
// Sans oublier bien sûr de renormaliser les poids (que leur somme soit 1)
|
||||
float weightSum = 0.f;
|
||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
||||
weightSum += tempWeights[j].bias;
|
||||
|
||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
||||
tempWeights[j].bias /= weightSum;
|
||||
|
||||
weightCount = NAZARA_UTILITY_SKINNING_MAX_WEIGHTS;
|
||||
}
|
||||
|
||||
vertices->weightCount = weightCount;
|
||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
||||
{
|
||||
if (j < weightCount)
|
||||
{
|
||||
// On donne une valeur aux poids présents
|
||||
vertices->weights[j] = tempWeights[j].bias;
|
||||
vertices->jointIndexes[j] = tempWeights[j].jointIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Et un poids de 0 sur le joint 0 pour les autres (nécessaire pour le GPU Skinning)
|
||||
// La raison est que le GPU ne tiendra pas compte du nombre de poids pour des raisons de performances.
|
||||
vertices->weights[j] = 0.f;
|
||||
vertices->jointIndexes[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vertices->position = finalPos;
|
||||
vertices->uv.Set(vertex.uv.x, (parameters.flipUVs) ? 1.f - vertex.uv.y : vertex.uv.y); // Inversion des UV si demandé
|
||||
vertices++;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
// Material
|
||||
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
|
||||
|
||||
// Submesh
|
||||
SkeletalMeshRef subMesh = SkeletalMesh::New(mesh);
|
||||
subMesh->Create(vertexBuffer);
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer);
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
subMesh->SetMaterialIndex(i);
|
||||
subMesh->SetPrimitiveMode(PrimitiveMode_TriangleList);
|
||||
|
||||
mesh->AddSubMesh(subMesh);
|
||||
|
||||
// Animation
|
||||
// Il est peut-être éventuellement possible que la probabilité que l'animation ait le même nom soit non-nulle.
|
||||
String path = stream.GetPath();
|
||||
if (!path.IsEmpty())
|
||||
{
|
||||
path.Replace(".md5mesh", ".md5anim", -8, String::CaseInsensitive);
|
||||
if (File::Exists(path))
|
||||
mesh->SetAnimation(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
mesh->SetMaterialCount(meshCount);
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
{
|
||||
const MD5MeshParser::Mesh& md5Mesh = meshes[i];
|
||||
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
||||
unsigned int vertexCount = md5Mesh.vertices.size();
|
||||
|
||||
// Submesh
|
||||
NzStaticMeshRef subMesh = NzStaticMesh::New(mesh);
|
||||
subMesh->Create(vertexBuffer);
|
||||
// Index buffer
|
||||
bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max());
|
||||
|
||||
if (parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage);
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer);
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
subMesh->SetMaterialIndex(i);
|
||||
IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
|
||||
IndexIterator index = indexMapper.begin();
|
||||
|
||||
mesh->AddSubMesh(subMesh);
|
||||
for (const MD5MeshParser::Triangle& triangle : md5Mesh.triangles)
|
||||
{
|
||||
// On les respécifie dans le bon ordre
|
||||
*index++ = triangle.x;
|
||||
*index++ = triangle.z;
|
||||
*index++ = triangle.y;
|
||||
}
|
||||
indexMapper.Unmap();
|
||||
|
||||
// Material
|
||||
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
|
||||
// Vertex buffer
|
||||
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage);
|
||||
BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);
|
||||
|
||||
MeshVertex* vertex = reinterpret_cast<MeshVertex*>(vertexMapper.GetPointer());
|
||||
for (const MD5MeshParser::Vertex& md5Vertex : md5Mesh.vertices)
|
||||
{
|
||||
// Skinning MD5 (Formule d'Id Tech)
|
||||
Vector3f finalPos(Vector3f::Zero());
|
||||
for (unsigned int j = 0; j < md5Vertex.weightCount; ++j)
|
||||
{
|
||||
const MD5MeshParser::Weight& weight = md5Mesh.weights[md5Vertex.startWeight + j];
|
||||
const MD5MeshParser::Joint& joint = joints[weight.joint];
|
||||
|
||||
finalPos += (joint.bindPos + joint.bindOrient*weight.pos) * weight.bias;
|
||||
}
|
||||
|
||||
// On retourne le modèle dans le bon sens
|
||||
vertex->position = scale * (rotationQuat * finalPos);
|
||||
vertex->uv.Set(md5Vertex.uv.x, (parameters.flipUVs) ? 1.f - md5Vertex.uv.y : md5Vertex.uv.y); // Inversion des UV si demandé
|
||||
vertex++;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
// Submesh
|
||||
StaticMeshRef subMesh = StaticMesh::New(mesh);
|
||||
subMesh->Create(vertexBuffer);
|
||||
|
||||
if (parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer);
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
subMesh->SetMaterialIndex(i);
|
||||
|
||||
mesh->AddSubMesh(subMesh);
|
||||
|
||||
// Material
|
||||
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
|
||||
}
|
||||
|
||||
if (parameters.center)
|
||||
mesh->Recenter();
|
||||
}
|
||||
|
||||
if (parameters.center)
|
||||
mesh->Recenter();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterMD5Mesh()
|
||||
{
|
||||
MeshLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
return true;
|
||||
void UnregisterMD5Mesh()
|
||||
{
|
||||
MeshLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_MD5Mesh_Register()
|
||||
{
|
||||
NzMeshLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_MD5Mesh_Unregister()
|
||||
{
|
||||
NzMeshLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_MD5Mesh_Register();
|
||||
void NzLoaders_MD5Mesh_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterMD5Mesh();
|
||||
void UnregisterMD5Mesh();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_MD5MESH_HPP
|
||||
|
||||
@@ -19,410 +19,413 @@
|
||||
#include <memory>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzMD5MeshParser::NzMD5MeshParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_keepLastLine(false),
|
||||
m_lineCount(0),
|
||||
m_meshIndex(0),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
namespace Nz
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | nzStreamOption_Text);
|
||||
}
|
||||
|
||||
NzMD5MeshParser::~NzMD5MeshParser()
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
nzTernary NzMD5MeshParser::Check()
|
||||
{
|
||||
if (Advance(false))
|
||||
MD5MeshParser::MD5MeshParser(InputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_keepLastLine(false),
|
||||
m_lineCount(0),
|
||||
m_meshIndex(0),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
{
|
||||
unsigned int version;
|
||||
if (std::sscanf(&m_currentLine[0], " MD5Version %u", &version) == 1)
|
||||
{
|
||||
if (version == 10)
|
||||
return nzTernary_True;
|
||||
}
|
||||
if ((m_streamFlags & StreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | StreamOption_Text);
|
||||
}
|
||||
|
||||
return nzTernary_False;
|
||||
}
|
||||
|
||||
const NzMD5MeshParser::Joint* NzMD5MeshParser::GetJoints() const
|
||||
{
|
||||
return m_joints.data();
|
||||
}
|
||||
|
||||
unsigned int NzMD5MeshParser::GetJointCount() const
|
||||
{
|
||||
return m_joints.size();
|
||||
}
|
||||
|
||||
const NzMD5MeshParser::Mesh* NzMD5MeshParser::GetMeshes() const
|
||||
{
|
||||
return m_meshes.data();
|
||||
}
|
||||
|
||||
unsigned int NzMD5MeshParser::GetMeshCount() const
|
||||
{
|
||||
return m_meshes.size();
|
||||
}
|
||||
|
||||
bool NzMD5MeshParser::Parse()
|
||||
{
|
||||
while (Advance(false))
|
||||
MD5MeshParser::~MD5MeshParser()
|
||||
{
|
||||
switch (m_currentLine[0])
|
||||
if ((m_streamFlags & StreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
Ternary MD5MeshParser::Check()
|
||||
{
|
||||
if (Advance(false))
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
case 'M': // MD5Version
|
||||
if (m_currentLine.GetWord(0) != "MD5Version")
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
unsigned int version;
|
||||
if (std::sscanf(&m_currentLine[0], " MD5Version %u", &version) == 1)
|
||||
{
|
||||
if (version == 10)
|
||||
return Ternary_True;
|
||||
}
|
||||
}
|
||||
|
||||
case 'c': // commandline
|
||||
if (m_currentLine.GetWord(0) != "commandline")
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
#endif
|
||||
return Ternary_False;
|
||||
}
|
||||
|
||||
case 'j': // joints
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_currentLine.StartsWith("joints {"))
|
||||
{
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
const MD5MeshParser::Joint* MD5MeshParser::GetJoints() const
|
||||
{
|
||||
return m_joints.data();
|
||||
}
|
||||
|
||||
if (!ParseJoints())
|
||||
{
|
||||
Error("Failed to parse joints");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
unsigned int MD5MeshParser::GetJointCount() const
|
||||
{
|
||||
return m_joints.size();
|
||||
}
|
||||
|
||||
case 'm': // mesh
|
||||
const MD5MeshParser::Mesh* MD5MeshParser::GetMeshes() const
|
||||
{
|
||||
return m_meshes.data();
|
||||
}
|
||||
|
||||
unsigned int MD5MeshParser::GetMeshCount() const
|
||||
{
|
||||
return m_meshes.size();
|
||||
}
|
||||
|
||||
bool MD5MeshParser::Parse()
|
||||
{
|
||||
while (Advance(false))
|
||||
{
|
||||
switch (m_currentLine[0])
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine != "mesh {")
|
||||
{
|
||||
UnrecognizedLine();
|
||||
case 'M': // MD5Version
|
||||
if (m_currentLine.GetWord(0) != "MD5Version")
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
|
||||
case 'c': // commandline
|
||||
if (m_currentLine.GetWord(0) != "commandline")
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_meshIndex >= m_meshes.size())
|
||||
{
|
||||
case 'j': // joints
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("More meshes than registred");
|
||||
if (!m_currentLine.StartsWith("joints {"))
|
||||
{
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_meshes.push_back(Mesh());
|
||||
if (!ParseJoints())
|
||||
{
|
||||
Error("Failed to parse joints");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm': // mesh
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine != "mesh {")
|
||||
{
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_meshIndex >= m_meshes.size())
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("More meshes than registred");
|
||||
#endif
|
||||
|
||||
m_meshes.push_back(Mesh());
|
||||
}
|
||||
|
||||
if (!ParseMesh())
|
||||
{
|
||||
NazaraError("Failed to parse mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_meshIndex++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ParseMesh())
|
||||
case 'n': // num[Frames/Joints]
|
||||
{
|
||||
NazaraError("Failed to parse mesh");
|
||||
unsigned int count;
|
||||
if (std::sscanf(&m_currentLine[0], "numJoints %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_joints.empty())
|
||||
Warning("Joint count is already defined");
|
||||
#endif
|
||||
|
||||
m_joints.resize(count);
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numMeshes %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_meshes.empty())
|
||||
Warning("Mesh count is already defined");
|
||||
#endif
|
||||
|
||||
m_meshes.resize(count);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MD5MeshParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete MD5 file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_meshIndex++;
|
||||
break;
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubStringTo("//"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
|
||||
case 'n': // num[Frames/Joints]
|
||||
{
|
||||
unsigned int count;
|
||||
if (std::sscanf(&m_currentLine[0], "numJoints %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_joints.empty())
|
||||
Warning("Joint count is already defined");
|
||||
#endif
|
||||
|
||||
m_joints.resize(count);
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numMeshes %u", &count) == 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_meshes.empty())
|
||||
Warning("Mesh count is already defined");
|
||||
#endif
|
||||
|
||||
m_meshes.resize(count);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzMD5MeshParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
void MD5MeshParser::Error(const String& message)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete MD5 file");
|
||||
NazaraError(message + " at line #" + String::Number(m_lineCount));
|
||||
}
|
||||
|
||||
bool MD5MeshParser::ParseJoints()
|
||||
{
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
{
|
||||
Error("Joint count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
unsigned int pos = m_currentLine.Find(' ');
|
||||
if (pos == String::npos)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
if (pos >= 64)
|
||||
{
|
||||
NazaraError("Joint name is too long (>= 64 characters)");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubStringTo("//"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
char name[64];
|
||||
if (std::sscanf(&m_currentLine[0], "%63s %d ( %f %f %f ) ( %f %f %f )", &name[0], &m_joints[i].parent,
|
||||
&m_joints[i].bindPos.x, &m_joints[i].bindPos.y, &m_joints[i].bindPos.z,
|
||||
&m_joints[i].bindOrient.x, &m_joints[i].bindOrient.y, &m_joints[i].bindOrient.z) != 8)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_joints[i].name = name;
|
||||
m_joints[i].name.Trim('"');
|
||||
|
||||
int parent = m_joints[i].parent;
|
||||
if (parent >= 0)
|
||||
{
|
||||
if (static_cast<unsigned int>(parent) >= jointCount)
|
||||
{
|
||||
Error("Joint's parent is out of bounds (" + String::Number(parent) + " >= " + String::Number(jointCount) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_joints[i].bindOrient.ComputeW(); // On calcule la composante W
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzMD5MeshParser::Error(const NzString& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
bool NzMD5MeshParser::ParseJoints()
|
||||
{
|
||||
unsigned int jointCount = m_joints.size();
|
||||
if (jointCount == 0)
|
||||
{
|
||||
Error("Joint count is invalid or missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
unsigned int pos = m_currentLine.Find(' ');
|
||||
if (pos == NzString::npos)
|
||||
if (m_currentLine != '}')
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Hierarchy braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
}
|
||||
|
||||
if (pos >= 64)
|
||||
{
|
||||
NazaraError("Joint name is too long (>= 64 characters)");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char name[64];
|
||||
if (std::sscanf(&m_currentLine[0], "%63s %d ( %f %f %f ) ( %f %f %f )", &name[0], &m_joints[i].parent,
|
||||
&m_joints[i].bindPos.x, &m_joints[i].bindPos.y, &m_joints[i].bindPos.z,
|
||||
&m_joints[i].bindOrient.x, &m_joints[i].bindOrient.y, &m_joints[i].bindOrient.z) != 8)
|
||||
bool MD5MeshParser::ParseMesh()
|
||||
{
|
||||
bool finished = false;
|
||||
while (!finished && Advance(false))
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_joints[i].name = name;
|
||||
m_joints[i].name.Trim('"');
|
||||
|
||||
int parent = m_joints[i].parent;
|
||||
if (parent >= 0)
|
||||
{
|
||||
if (static_cast<unsigned int>(parent) >= jointCount)
|
||||
switch (m_currentLine[0])
|
||||
{
|
||||
Error("Joint's parent is out of bounds (" + NzString::Number(parent) + " >= " + NzString::Number(jointCount) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case '}':
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
m_joints[i].bindOrient.ComputeW(); // On calcule la composante W
|
||||
}
|
||||
case 's': // shader
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_currentLine.StartsWith("shader "))
|
||||
{
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!Advance())
|
||||
return false;
|
||||
m_meshes[m_meshIndex].shader = m_currentLine.SubString(7);
|
||||
m_meshes[m_meshIndex].shader.Trim('"');
|
||||
break;
|
||||
|
||||
if (m_currentLine != '}')
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
Warning("Hierarchy braces closing not found");
|
||||
#endif
|
||||
|
||||
// On tente de survivre à l'erreur
|
||||
m_keepLastLine = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzMD5MeshParser::ParseMesh()
|
||||
{
|
||||
bool finished = false;
|
||||
while (!finished && Advance(false))
|
||||
{
|
||||
switch (m_currentLine[0])
|
||||
{
|
||||
case '}':
|
||||
finished = true;
|
||||
break;
|
||||
|
||||
case 's': // shader
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!m_currentLine.StartsWith("shader "))
|
||||
case 'n': // num[tris/verts]
|
||||
{
|
||||
UnrecognizedLine();
|
||||
unsigned int count;
|
||||
if (std::sscanf(&m_currentLine[0], "numtris %u", &count) == 1)
|
||||
{
|
||||
m_meshes[m_meshIndex].triangles.resize(count);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Triangle& triangle = m_meshes[m_meshIndex].triangles[i];
|
||||
unsigned int index;
|
||||
if (std::sscanf(&m_currentLine[0], "tri %u %u %u %u", &index, &triangle.x, &triangle.y, &triangle.z) != 4)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index != i)
|
||||
{
|
||||
Error("Unexpected triangle index (expected " + String::Number(i) + ", got " + String::Number(index) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numverts %u", &count) == 1)
|
||||
{
|
||||
m_meshes[m_meshIndex].vertices.resize(count);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Vertex& vertex = m_meshes[m_meshIndex].vertices[i];
|
||||
unsigned int index;
|
||||
if (std::sscanf(&m_currentLine[0], "vert %u ( %f %f ) %u %u", &index, &vertex.uv.x, &vertex.uv.y, &vertex.startWeight, &vertex.weightCount) != 5)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index != i)
|
||||
{
|
||||
Error("Unexpected vertex index (expected " + String::Number(i) + ", got " + String::Number(index) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numweights %u", &count) == 1)
|
||||
{
|
||||
m_meshes[m_meshIndex].weights.resize(count);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Weight& weight = m_meshes[m_meshIndex].weights[i];
|
||||
unsigned int index;
|
||||
if (std::sscanf(&m_currentLine[0], "weight %u %u %f ( %f %f %f )", &index, &weight.joint, &weight.bias,
|
||||
&weight.pos.x, &weight.pos.y, &weight.pos.z) != 6)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index != i)
|
||||
{
|
||||
Error("Unexpected weight index (expected " + String::Number(i) + ", got " + String::Number(index) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_meshes[m_meshIndex].shader = m_currentLine.SubString(7);
|
||||
m_meshes[m_meshIndex].shader.Trim('"');
|
||||
break;
|
||||
|
||||
case 'n': // num[tris/verts]
|
||||
{
|
||||
unsigned int count;
|
||||
if (std::sscanf(&m_currentLine[0], "numtris %u", &count) == 1)
|
||||
{
|
||||
m_meshes[m_meshIndex].triangles.resize(count);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Triangle& triangle = m_meshes[m_meshIndex].triangles[i];
|
||||
unsigned int index;
|
||||
if (std::sscanf(&m_currentLine[0], "tri %u %u %u %u", &index, &triangle.x, &triangle.y, &triangle.z) != 4)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index != i)
|
||||
{
|
||||
Error("Unexpected triangle index (expected " + NzString::Number(i) + ", got " + NzString::Number(index) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numverts %u", &count) == 1)
|
||||
{
|
||||
m_meshes[m_meshIndex].vertices.resize(count);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Vertex& vertex = m_meshes[m_meshIndex].vertices[i];
|
||||
unsigned int index;
|
||||
if (std::sscanf(&m_currentLine[0], "vert %u ( %f %f ) %u %u", &index, &vertex.uv.x, &vertex.uv.y, &vertex.startWeight, &vertex.weightCount) != 5)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index != i)
|
||||
{
|
||||
Error("Unexpected vertex index (expected " + NzString::Number(i) + ", got " + NzString::Number(index) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (std::sscanf(&m_currentLine[0], "numweights %u", &count) == 1)
|
||||
{
|
||||
m_meshes[m_meshIndex].weights.resize(count);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Weight& weight = m_meshes[m_meshIndex].weights[i];
|
||||
unsigned int index;
|
||||
if (std::sscanf(&m_currentLine[0], "weight %u %u %f ( %f %f %f )", &index, &weight.joint, &weight.bias,
|
||||
&weight.pos.x, &weight.pos.y, &weight.pos.z) != 6)
|
||||
{
|
||||
UnrecognizedLine(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index != i)
|
||||
{
|
||||
Error("Unexpected weight index (expected " + NzString::Number(i) + ", got " + NzString::Number(index) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_meshes[m_meshIndex].triangles.empty())
|
||||
{
|
||||
NazaraError("Mesh has no triangles");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_meshes[m_meshIndex].vertices.empty())
|
||||
{
|
||||
NazaraError("Mesh has no vertices");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_meshes[m_meshIndex].weights.empty())
|
||||
{
|
||||
NazaraError("Mesh has no weights");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!finished)
|
||||
Warning("Mesh braces closing not found");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_meshes[m_meshIndex].triangles.empty())
|
||||
void MD5MeshParser::Warning(const String& message)
|
||||
{
|
||||
NazaraError("Mesh has no triangles");
|
||||
return false;
|
||||
NazaraWarning(message + " at line #" + String::Number(m_lineCount));
|
||||
}
|
||||
|
||||
if (m_meshes[m_meshIndex].vertices.empty())
|
||||
void MD5MeshParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
NazaraError("Mesh has no vertices");
|
||||
return false;
|
||||
}
|
||||
String message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (m_meshes[m_meshIndex].weights.empty())
|
||||
{
|
||||
NazaraError("Mesh has no weights");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (!finished)
|
||||
Warning("Mesh braces closing not found");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
}
|
||||
|
||||
void NzMD5MeshParser::Warning(const NzString& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzMD5MeshParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
NzString message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
}
|
||||
|
||||
@@ -10,324 +10,327 @@
|
||||
#include <memory>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzMTLParser::NzMTLParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
namespace Nz
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | nzStreamOption_Text);
|
||||
}
|
||||
|
||||
NzMTLParser::~NzMTLParser()
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
const NzMTLParser::Material* NzMTLParser::GetMaterial(const NzString& materialName) const
|
||||
{
|
||||
auto it = m_materials.find(materialName);
|
||||
if (it != m_materials.end())
|
||||
return &it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::unordered_map<NzString, NzMTLParser::Material>& NzMTLParser::GetMaterials() const
|
||||
{
|
||||
return m_materials;
|
||||
}
|
||||
|
||||
bool NzMTLParser::Parse()
|
||||
{
|
||||
m_keepLastLine = false;
|
||||
m_lineCount = 0;
|
||||
m_materials.clear();
|
||||
|
||||
Material* currentMaterial = nullptr;
|
||||
|
||||
while (Advance(false))
|
||||
MTLParser::MTLParser(InputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
{
|
||||
NzString keyword = m_currentLine.GetWord(0).ToLower();
|
||||
if (keyword == "ka")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
if ((m_streamFlags & StreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | StreamOption_Text);
|
||||
}
|
||||
|
||||
currentMaterial->ambient = NzColor(static_cast<nzUInt8>(r*255.f), static_cast<nzUInt8>(g*255.f), static_cast<nzUInt8>(b*255.f));
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "kd")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
MTLParser::~MTLParser()
|
||||
{
|
||||
if ((m_streamFlags & StreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
currentMaterial->diffuse = NzColor(static_cast<nzUInt8>(r*255.f), static_cast<nzUInt8>(g*255.f), static_cast<nzUInt8>(b*255.f));
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ks")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->specular = NzColor(static_cast<nzUInt8>(r*255.f), static_cast<nzUInt8>(g*255.f), static_cast<nzUInt8>(b*255.f));
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ni")
|
||||
{
|
||||
float density;
|
||||
if (std::sscanf(&m_currentLine[3], "%f", &density) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->refractionIndex = density;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ns")
|
||||
{
|
||||
float coef;
|
||||
if (std::sscanf(&m_currentLine[3], "%f", &coef) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->shininess = coef;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == 'd')
|
||||
{
|
||||
float alpha;
|
||||
if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alpha = alpha;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "tr")
|
||||
{
|
||||
float alpha;
|
||||
if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alpha = 1.f - alpha; // tr vaut pour la "valeur de transparence", 0 = opaque
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "illum")
|
||||
{
|
||||
unsigned int model;
|
||||
if (std::sscanf(&m_currentLine[6], "%u", &model) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->illumModel = model;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_ka")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != NzString::npos)
|
||||
{
|
||||
NzString map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->ambientMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_kd")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != NzString::npos)
|
||||
{
|
||||
NzString map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->diffuseMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_ks")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != NzString::npos)
|
||||
{
|
||||
NzString map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->specularMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_bump" || keyword == "bump")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != NzString::npos)
|
||||
{
|
||||
NzString map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->bumpMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_d")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != NzString::npos)
|
||||
{
|
||||
NzString map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alphaMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_decal" || keyword == "decal")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != NzString::npos)
|
||||
{
|
||||
NzString map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->decalMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_disp" || keyword == "disp")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != NzString::npos)
|
||||
{
|
||||
NzString map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->displacementMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_refl" || keyword == "refl")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != NzString::npos)
|
||||
{
|
||||
NzString map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->reflectionMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "newmtl")
|
||||
{
|
||||
NzString materialName = m_currentLine.SubString(m_currentLine.GetWordPosition(1));
|
||||
if (!materialName.IsEmpty())
|
||||
currentMaterial = &m_materials[materialName];
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
const MTLParser::Material* MTLParser::GetMaterial(const String& materialName) const
|
||||
{
|
||||
auto it = m_materials.find(materialName);
|
||||
if (it != m_materials.end())
|
||||
return &it->second;
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzMTLParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
const std::unordered_map<String, MTLParser::Material>& MTLParser::GetMaterials() const
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete MTL file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubStringTo("#"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
return m_materials;
|
||||
}
|
||||
else
|
||||
|
||||
bool MTLParser::Parse()
|
||||
{
|
||||
m_keepLastLine = false;
|
||||
m_lineCount = 0;
|
||||
m_materials.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzMTLParser::Error(const NzString& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzMTLParser::Warning(const NzString& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzMTLParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
NzString message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
Material* currentMaterial = nullptr;
|
||||
|
||||
while (Advance(false))
|
||||
{
|
||||
String keyword = m_currentLine.GetWord(0).ToLower();
|
||||
if (keyword == "ka")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->ambient = Color(static_cast<UInt8>(r*255.f), static_cast<UInt8>(g*255.f), static_cast<UInt8>(b*255.f));
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "kd")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->diffuse = Color(static_cast<UInt8>(r*255.f), static_cast<UInt8>(g*255.f), static_cast<UInt8>(b*255.f));
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ks")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->specular = Color(static_cast<UInt8>(r*255.f), static_cast<UInt8>(g*255.f), static_cast<UInt8>(b*255.f));
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ni")
|
||||
{
|
||||
float density;
|
||||
if (std::sscanf(&m_currentLine[3], "%f", &density) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->refractionIndex = density;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ns")
|
||||
{
|
||||
float coef;
|
||||
if (std::sscanf(&m_currentLine[3], "%f", &coef) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->shininess = coef;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == 'd')
|
||||
{
|
||||
float alpha;
|
||||
if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alpha = alpha;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "tr")
|
||||
{
|
||||
float alpha;
|
||||
if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alpha = 1.f - alpha; // tr vaut pour la "valeur de transparence", 0 = opaque
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "illum")
|
||||
{
|
||||
unsigned int model;
|
||||
if (std::sscanf(&m_currentLine[6], "%u", &model) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->illumModel = model;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_ka")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != String::npos)
|
||||
{
|
||||
String map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->ambientMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_kd")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != String::npos)
|
||||
{
|
||||
String map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->diffuseMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_ks")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != String::npos)
|
||||
{
|
||||
String map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->specularMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_bump" || keyword == "bump")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != String::npos)
|
||||
{
|
||||
String map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->bumpMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_d")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != String::npos)
|
||||
{
|
||||
String map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alphaMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_decal" || keyword == "decal")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != String::npos)
|
||||
{
|
||||
String map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->decalMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_disp" || keyword == "disp")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != String::npos)
|
||||
{
|
||||
String map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->displacementMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "map_refl" || keyword == "refl")
|
||||
{
|
||||
unsigned int mapPos = m_currentLine.GetWordPosition(1);
|
||||
if (mapPos != String::npos)
|
||||
{
|
||||
String map = m_currentLine.SubString(mapPos);
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->reflectionMap = map;
|
||||
}
|
||||
}
|
||||
else if (keyword == "newmtl")
|
||||
{
|
||||
String materialName = m_currentLine.SubString(m_currentLine.GetWordPosition(1));
|
||||
if (!materialName.IsEmpty())
|
||||
currentMaterial = &m_materials[materialName];
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MTLParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete MTL file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubStringTo("#"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MTLParser::Error(const String& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + String::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void MTLParser::Warning(const String& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + String::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void MTLParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
String message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,446 +11,449 @@
|
||||
#include <unordered_map>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzOBJParser::NzOBJParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
namespace Nz
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | nzStreamOption_Text);
|
||||
}
|
||||
|
||||
NzOBJParser::~NzOBJParser()
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
const NzString* NzOBJParser::GetMaterials() const
|
||||
{
|
||||
return m_materials.data();
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetMaterialCount() const
|
||||
{
|
||||
return m_materials.size();
|
||||
}
|
||||
|
||||
const NzOBJParser::Mesh* NzOBJParser::GetMeshes() const
|
||||
{
|
||||
return m_meshes.data();
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetMeshCount() const
|
||||
{
|
||||
return m_meshes.size();
|
||||
}
|
||||
|
||||
const NzString& NzOBJParser::GetMtlLib() const
|
||||
{
|
||||
return m_mtlLib;
|
||||
}
|
||||
|
||||
const NzVector3f* NzOBJParser::GetNormals() const
|
||||
{
|
||||
return m_normals.data();
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetNormalCount() const
|
||||
{
|
||||
return m_normals.size();
|
||||
}
|
||||
|
||||
const NzVector4f* NzOBJParser::GetPositions() const
|
||||
{
|
||||
return m_positions.data();
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetPositionCount() const
|
||||
{
|
||||
return m_positions.size();
|
||||
}
|
||||
|
||||
const NzVector3f* NzOBJParser::GetTexCoords() const
|
||||
{
|
||||
return m_texCoords.data();
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetTexCoordCount() const
|
||||
{
|
||||
return m_texCoords.size();
|
||||
}
|
||||
|
||||
bool NzOBJParser::Parse()
|
||||
{
|
||||
NzString matName, meshName;
|
||||
matName = meshName = "default";
|
||||
m_keepLastLine = false;
|
||||
m_lineCount = 0;
|
||||
m_meshes.clear();
|
||||
m_mtlLib.Clear();
|
||||
|
||||
m_normals.clear();
|
||||
m_positions.clear();
|
||||
m_texCoords.clear();
|
||||
|
||||
// Beaucoup de meshs font plus de 100 sommets, préparons le terrain
|
||||
m_normals.reserve(100);
|
||||
m_positions.reserve(100);
|
||||
m_texCoords.reserve(100);
|
||||
|
||||
// On va regrouper les meshs par nom et par matériau
|
||||
using FaceVec = std::vector<Face>;
|
||||
using MatPair = std::pair<FaceVec, unsigned int>;
|
||||
std::unordered_map<NzString, std::unordered_map<NzString, MatPair>> meshes;
|
||||
|
||||
unsigned int matIndex = 0;
|
||||
auto GetMaterial = [&meshes, &matIndex] (const NzString& mesh, const NzString& material) -> FaceVec*
|
||||
OBJParser::OBJParser(InputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
{
|
||||
auto& map = meshes[mesh];
|
||||
auto it = map.find(material);
|
||||
if (it == map.end())
|
||||
it = map.insert(std::make_pair(material, MatPair(FaceVec(), matIndex++))).first;
|
||||
if ((m_streamFlags & StreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | StreamOption_Text);
|
||||
}
|
||||
|
||||
return &(it->second.first);
|
||||
};
|
||||
|
||||
// On prépare le mesh par défaut
|
||||
FaceVec* currentMesh = nullptr;
|
||||
|
||||
while (Advance(false))
|
||||
OBJParser::~OBJParser()
|
||||
{
|
||||
switch (std::tolower(m_currentLine[0]))
|
||||
if ((m_streamFlags & StreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
const String* OBJParser::GetMaterials() const
|
||||
{
|
||||
return m_materials.data();
|
||||
}
|
||||
|
||||
unsigned int OBJParser::GetMaterialCount() const
|
||||
{
|
||||
return m_materials.size();
|
||||
}
|
||||
|
||||
const OBJParser::Mesh* OBJParser::GetMeshes() const
|
||||
{
|
||||
return m_meshes.data();
|
||||
}
|
||||
|
||||
unsigned int OBJParser::GetMeshCount() const
|
||||
{
|
||||
return m_meshes.size();
|
||||
}
|
||||
|
||||
const String& OBJParser::GetMtlLib() const
|
||||
{
|
||||
return m_mtlLib;
|
||||
}
|
||||
|
||||
const Vector3f* OBJParser::GetNormals() const
|
||||
{
|
||||
return m_normals.data();
|
||||
}
|
||||
|
||||
unsigned int OBJParser::GetNormalCount() const
|
||||
{
|
||||
return m_normals.size();
|
||||
}
|
||||
|
||||
const Vector4f* OBJParser::GetPositions() const
|
||||
{
|
||||
return m_positions.data();
|
||||
}
|
||||
|
||||
unsigned int OBJParser::GetPositionCount() const
|
||||
{
|
||||
return m_positions.size();
|
||||
}
|
||||
|
||||
const Vector3f* OBJParser::GetTexCoords() const
|
||||
{
|
||||
return m_texCoords.data();
|
||||
}
|
||||
|
||||
unsigned int OBJParser::GetTexCoordCount() const
|
||||
{
|
||||
return m_texCoords.size();
|
||||
}
|
||||
|
||||
bool OBJParser::Parse()
|
||||
{
|
||||
String matName, meshName;
|
||||
matName = meshName = "default";
|
||||
m_keepLastLine = false;
|
||||
m_lineCount = 0;
|
||||
m_meshes.clear();
|
||||
m_mtlLib.Clear();
|
||||
|
||||
m_normals.clear();
|
||||
m_positions.clear();
|
||||
m_texCoords.clear();
|
||||
|
||||
// Beaucoup de meshs font plus de 100 sommets, préparons le terrain
|
||||
m_normals.reserve(100);
|
||||
m_positions.reserve(100);
|
||||
m_texCoords.reserve(100);
|
||||
|
||||
// On va regrouper les meshs par nom et par matériau
|
||||
using FaceVec = std::vector<Face>;
|
||||
using MatPair = std::pair<FaceVec, unsigned int>;
|
||||
std::unordered_map<String, std::unordered_map<String, MatPair>> meshes;
|
||||
|
||||
unsigned int matIndex = 0;
|
||||
auto GetMaterial = [&meshes, &matIndex] (const String& mesh, const String& material) -> FaceVec*
|
||||
{
|
||||
case 'f': // Une face
|
||||
auto& map = meshes[mesh];
|
||||
auto it = map.find(material);
|
||||
if (it == map.end())
|
||||
it = map.insert(std::make_pair(material, MatPair(FaceVec(), matIndex++))).first;
|
||||
|
||||
return &(it->second.first);
|
||||
};
|
||||
|
||||
// On prépare le mesh par défaut
|
||||
FaceVec* currentMesh = nullptr;
|
||||
|
||||
while (Advance(false))
|
||||
{
|
||||
switch (std::tolower(m_currentLine[0]))
|
||||
{
|
||||
if (m_currentLine.GetSize() < 7) // Le minimum syndical pour définir une face de trois sommets (f 1 2 3)
|
||||
case 'f': // Une face
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int vertexCount = m_currentLine.Count(' ');
|
||||
if (vertexCount < 3)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
Face face;
|
||||
face.vertices.resize(vertexCount);
|
||||
|
||||
bool error = false;
|
||||
unsigned int pos = 2;
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
int offset;
|
||||
int& n = face.vertices[i].normal;
|
||||
int& p = face.vertices[i].position;
|
||||
int& t = face.vertices[i].texCoord;
|
||||
|
||||
if (std::sscanf(&m_currentLine[pos], "%d/%d/%d%n", &p, &t, &n, &offset) != 3)
|
||||
if (m_currentLine.GetSize() < 7) // Le minimum syndical pour définir une face de trois sommets (f 1 2 3)
|
||||
{
|
||||
if (std::sscanf(&m_currentLine[pos], "%d//%d%n", &p, &n, &offset) != 2)
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int vertexCount = m_currentLine.Count(' ');
|
||||
if (vertexCount < 3)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
Face face;
|
||||
face.vertices.resize(vertexCount);
|
||||
|
||||
bool error = false;
|
||||
unsigned int pos = 2;
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
int offset;
|
||||
int& n = face.vertices[i].normal;
|
||||
int& p = face.vertices[i].position;
|
||||
int& t = face.vertices[i].texCoord;
|
||||
|
||||
if (std::sscanf(&m_currentLine[pos], "%d/%d/%d%n", &p, &t, &n, &offset) != 3)
|
||||
{
|
||||
if (std::sscanf(&m_currentLine[pos], "%d/%d%n", &p, &t, &offset) != 2)
|
||||
if (std::sscanf(&m_currentLine[pos], "%d//%d%n", &p, &n, &offset) != 2)
|
||||
{
|
||||
if (std::sscanf(&m_currentLine[pos], "%d%n", &p, &offset) != 1)
|
||||
if (std::sscanf(&m_currentLine[pos], "%d/%d%n", &p, &t, &offset) != 2)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
error = true;
|
||||
break;
|
||||
if (std::sscanf(&m_currentLine[pos], "%d%n", &p, &offset) != 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = 0;
|
||||
t = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
n = 0;
|
||||
t = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
n = 0;
|
||||
t = 0;
|
||||
}
|
||||
else
|
||||
t = 0;
|
||||
}
|
||||
|
||||
if (p < 0)
|
||||
{
|
||||
p += m_positions.size();
|
||||
if (p < 0)
|
||||
{
|
||||
Error("Vertex index out of range (" + NzString::Number(p) + " < 0");
|
||||
error = true;
|
||||
break;
|
||||
p += m_positions.size();
|
||||
if (p < 0)
|
||||
{
|
||||
Error("Vertex index out of range (" + String::Number(p) + " < 0");
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
p--;
|
||||
else
|
||||
p--;
|
||||
|
||||
if (n < 0)
|
||||
{
|
||||
n += m_normals.size();
|
||||
if (n < 0)
|
||||
{
|
||||
Error("Vertex index out of range (" + NzString::Number(n) + " < 0");
|
||||
error = true;
|
||||
break;
|
||||
n += m_normals.size();
|
||||
if (n < 0)
|
||||
{
|
||||
Error("Vertex index out of range (" + String::Number(n) + " < 0");
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
n--;
|
||||
else
|
||||
n--;
|
||||
|
||||
if (t < 0)
|
||||
{
|
||||
t += m_texCoords.size();
|
||||
if (t < 0)
|
||||
{
|
||||
Error("Vertex index out of range (" + NzString::Number(t) + " < 0");
|
||||
t += m_texCoords.size();
|
||||
if (t < 0)
|
||||
{
|
||||
Error("Vertex index out of range (" + String::Number(t) + " < 0");
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
t--;
|
||||
|
||||
if (static_cast<unsigned int>(p) >= m_positions.size())
|
||||
{
|
||||
Error("Vertex index out of range (" + String::Number(p) + " >= " + String::Number(m_positions.size()) + ')');
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
else if (n >= 0 && static_cast<unsigned int>(n) >= m_normals.size())
|
||||
{
|
||||
Error("Normal index out of range (" + String::Number(n) + " >= " + String::Number(m_normals.size()) + ')');
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
else if (t >= 0 && static_cast<unsigned int>(t) >= m_texCoords.size())
|
||||
{
|
||||
Error("TexCoord index out of range (" + String::Number(t) + " >= " + String::Number(m_texCoords.size()) + ')');
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += offset;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
if (!currentMesh)
|
||||
currentMesh = GetMaterial(meshName, matName);
|
||||
|
||||
currentMesh->push_back(std::move(face));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'm':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine.GetWord(0).ToLower() != "mtllib")
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
m_mtlLib = m_currentLine.SubString(m_currentLine.GetWordPosition(1));
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case 'o':
|
||||
{
|
||||
if (m_currentLine.GetSize() <= 2 || m_currentLine[1] != ' ')
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
String objectName = m_currentLine.SubString(m_currentLine.GetWordPosition(1));
|
||||
if (objectName.IsEmpty())
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
meshName = objectName;
|
||||
currentMesh = GetMaterial(meshName, matName);
|
||||
break;
|
||||
}
|
||||
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
case 's':
|
||||
if (m_currentLine.GetSize() <= 2 || m_currentLine[1] == ' ')
|
||||
{
|
||||
String param = m_currentLine.SubString(2);
|
||||
if (param != "all" && param != "on" && param != "off" && !param.IsNumber())
|
||||
UnrecognizedLine();
|
||||
}
|
||||
else
|
||||
t--;
|
||||
|
||||
if (static_cast<unsigned int>(p) >= m_positions.size())
|
||||
{
|
||||
Error("Vertex index out of range (" + NzString::Number(p) + " >= " + NzString::Number(m_positions.size()) + ')');
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
else if (n >= 0 && static_cast<unsigned int>(n) >= m_normals.size())
|
||||
{
|
||||
Error("Normal index out of range (" + NzString::Number(n) + " >= " + NzString::Number(m_normals.size()) + ')');
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
else if (t >= 0 && static_cast<unsigned int>(t) >= m_texCoords.size())
|
||||
{
|
||||
Error("TexCoord index out of range (" + NzString::Number(t) + " >= " + NzString::Number(m_texCoords.size()) + ')');
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += offset;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
if (!currentMesh)
|
||||
currentMesh = GetMaterial(meshName, matName);
|
||||
|
||||
currentMesh->push_back(std::move(face));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'm':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine.GetWord(0).ToLower() != "mtllib")
|
||||
UnrecognizedLine();
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
#endif
|
||||
|
||||
m_mtlLib = m_currentLine.SubString(m_currentLine.GetWordPosition(1));
|
||||
break;
|
||||
case 'u':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine.GetWord(0) != "usemtl")
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
case 'g':
|
||||
case 'o':
|
||||
{
|
||||
if (m_currentLine.GetSize() <= 2 || m_currentLine[1] != ' ')
|
||||
matName = m_currentLine.SubString(m_currentLine.GetWordPosition(1));
|
||||
if (matName.IsEmpty())
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
currentMesh = GetMaterial(meshName, matName);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
{
|
||||
String word = m_currentLine.GetWord(0).ToLower();
|
||||
if (word == 'v')
|
||||
{
|
||||
Vector4f vertex(Vector3f::Zero(), 1.f);
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[2], "%f %f %f %f", &vertex.x, &vertex.y, &vertex.z, &vertex.w);
|
||||
if (paramCount >= 3)
|
||||
m_positions.push_back(vertex);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (word == "vn")
|
||||
{
|
||||
Vector3f normal(Vector3f::Zero());
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[3], "%f %f %f", &normal.x, &normal.y, &normal.z);
|
||||
if (paramCount == 3)
|
||||
m_normals.push_back(normal);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (word == "vt")
|
||||
{
|
||||
Vector3f uvw(Vector3f::Zero());
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[3], "%f %f %f", &uvw.x, &uvw.y, &uvw.z);
|
||||
if (paramCount >= 2)
|
||||
m_texCoords.push_back(uvw);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
NzString objectName = m_currentLine.SubString(m_currentLine.GetWordPosition(1));
|
||||
if (objectName.IsEmpty())
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
meshName = objectName;
|
||||
currentMesh = GetMaterial(meshName, matName);
|
||||
break;
|
||||
}
|
||||
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
case 's':
|
||||
if (m_currentLine.GetSize() <= 2 || m_currentLine[1] == ' ')
|
||||
{
|
||||
NzString param = m_currentLine.SubString(2);
|
||||
if (param != "all" && param != "on" && param != "off" && !param.IsNumber())
|
||||
UnrecognizedLine();
|
||||
}
|
||||
else
|
||||
UnrecognizedLine();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'u':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine.GetWord(0) != "usemtl")
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
matName = m_currentLine.SubString(m_currentLine.GetWordPosition(1));
|
||||
if (matName.IsEmpty())
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
currentMesh = GetMaterial(meshName, matName);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
{
|
||||
NzString word = m_currentLine.GetWord(0).ToLower();
|
||||
if (word == 'v')
|
||||
{
|
||||
NzVector4f vertex(NzVector3f::Zero(), 1.f);
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[2], "%f %f %f %f", &vertex.x, &vertex.y, &vertex.z, &vertex.w);
|
||||
if (paramCount >= 3)
|
||||
m_positions.push_back(vertex);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (word == "vn")
|
||||
{
|
||||
NzVector3f normal(NzVector3f::Zero());
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[3], "%f %f %f", &normal.x, &normal.y, &normal.z);
|
||||
if (paramCount == 3)
|
||||
m_normals.push_back(normal);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (word == "vt")
|
||||
{
|
||||
NzVector3f uvw(NzVector3f::Zero());
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[3], "%f %f %f", &uvw.x, &uvw.y, &uvw.z);
|
||||
if (paramCount >= 2)
|
||||
m_texCoords.push_back(uvw);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<NzString, unsigned int> materials;
|
||||
m_materials.resize(matIndex);
|
||||
std::unordered_map<String, unsigned int> materials;
|
||||
m_materials.resize(matIndex);
|
||||
|
||||
for (auto& meshIt : meshes)
|
||||
{
|
||||
for (auto& matIt : meshIt.second)
|
||||
for (auto& meshIt : meshes)
|
||||
{
|
||||
auto& faceVec = matIt.second.first;
|
||||
unsigned int index = matIt.second.second;
|
||||
if (!faceVec.empty())
|
||||
for (auto& matIt : meshIt.second)
|
||||
{
|
||||
Mesh mesh;
|
||||
mesh.faces = std::move(faceVec);
|
||||
mesh.name = meshIt.first;
|
||||
|
||||
auto it = materials.find(matIt.first);
|
||||
if (it == materials.end())
|
||||
auto& faceVec = matIt.second.first;
|
||||
unsigned int index = matIt.second.second;
|
||||
if (!faceVec.empty())
|
||||
{
|
||||
mesh.material = index;
|
||||
materials[matIt.first] = index;
|
||||
m_materials[index] = matIt.first;
|
||||
Mesh mesh;
|
||||
mesh.faces = std::move(faceVec);
|
||||
mesh.name = meshIt.first;
|
||||
|
||||
auto it = materials.find(matIt.first);
|
||||
if (it == materials.end())
|
||||
{
|
||||
mesh.material = index;
|
||||
materials[matIt.first] = index;
|
||||
m_materials[index] = matIt.first;
|
||||
}
|
||||
else
|
||||
mesh.material = it->second;
|
||||
|
||||
m_meshes.emplace_back(std::move(mesh));
|
||||
}
|
||||
else
|
||||
mesh.material = it->second;
|
||||
|
||||
m_meshes.emplace_back(std::move(mesh));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_meshes.empty())
|
||||
{
|
||||
NazaraError("No meshes");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzOBJParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
{
|
||||
do
|
||||
if (m_meshes.empty())
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete OBJ file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubStringTo("#"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
NazaraError("No meshes");
|
||||
return false;
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzOBJParser::Error(const NzString& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzOBJParser::Warning(const NzString& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzOBJParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
NzString message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
bool OBJParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete OBJ file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubStringTo("#"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OBJParser::Error(const String& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + String::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void OBJParser::Warning(const String& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + String::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void OBJParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
String message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,154 +12,101 @@
|
||||
|
||||
// Auteur du loader original : David Henry
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
struct pcx_header
|
||||
namespace
|
||||
{
|
||||
nzUInt8 manufacturer;
|
||||
nzUInt8 version;
|
||||
nzUInt8 encoding;
|
||||
nzUInt8 bitsPerPixel;
|
||||
|
||||
nzUInt16 xmin, ymin;
|
||||
nzUInt16 xmax, ymax;
|
||||
nzUInt16 horzRes, vertRes;
|
||||
|
||||
nzUInt8 palette[48];
|
||||
nzUInt8 reserved;
|
||||
nzUInt8 numColorPlanes;
|
||||
|
||||
nzUInt16 bytesPerScanLine;
|
||||
nzUInt16 paletteType;
|
||||
nzUInt16 horzSize, vertSize;
|
||||
|
||||
nzUInt8 padding[54];
|
||||
};
|
||||
|
||||
static_assert(sizeof(pcx_header) == (6+48+54)*sizeof(nzUInt8) + 10*sizeof(nzUInt16), "pcx_header struct must be packed");
|
||||
|
||||
bool IsSupported(const NzString& extension)
|
||||
{
|
||||
return (extension == "pcx");
|
||||
}
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzImageParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
nzUInt8 manufacturer;
|
||||
if (stream.Read(&manufacturer, 1) == 1)
|
||||
struct pcx_header
|
||||
{
|
||||
if (manufacturer == 0x0a)
|
||||
return nzTernary_True;
|
||||
UInt8 manufacturer;
|
||||
UInt8 version;
|
||||
UInt8 encoding;
|
||||
UInt8 bitsPerPixel;
|
||||
|
||||
UInt16 xmin, ymin;
|
||||
UInt16 xmax, ymax;
|
||||
UInt16 horzRes, vertRes;
|
||||
|
||||
UInt8 palette[48];
|
||||
UInt8 reserved;
|
||||
UInt8 numColorPlanes;
|
||||
|
||||
UInt16 bytesPerScanLine;
|
||||
UInt16 paletteType;
|
||||
UInt16 horzSize, vertSize;
|
||||
|
||||
UInt8 padding[54];
|
||||
};
|
||||
|
||||
static_assert(sizeof(pcx_header) == (6+48+54)*sizeof(UInt8) + 10*sizeof(UInt16), "pcx_header struct must be packed");
|
||||
|
||||
bool IsSupported(const String& extension)
|
||||
{
|
||||
return (extension == "pcx");
|
||||
}
|
||||
|
||||
return nzTernary_False;
|
||||
}
|
||||
|
||||
bool Load(NzImage* image, NzInputStream& stream, const NzImageParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
pcx_header header;
|
||||
if (stream.Read(&header, sizeof(pcx_header)) != sizeof(pcx_header))
|
||||
Ternary Check(InputStream& stream, const ImageParams& parameters)
|
||||
{
|
||||
NazaraError("Failed to read header");
|
||||
return false;
|
||||
}
|
||||
NazaraUnused(parameters);
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
// Les fichiers PCX sont en little endian
|
||||
NzByteSwap(&header.xmin, sizeof(nzUInt16));
|
||||
NzByteSwap(&header.ymin, sizeof(nzUInt16));
|
||||
NzByteSwap(&header.xmax, sizeof(nzUInt16));
|
||||
NzByteSwap(&header.ymax, sizeof(nzUInt16));
|
||||
NzByteSwap(&header.horzRes, sizeof(nzUInt16));
|
||||
NzByteSwap(&header.vertRes, sizeof(nzUInt16));
|
||||
|
||||
NzByteSwap(&header.bytesPerScanLine, sizeof(nzUInt16));
|
||||
NzByteSwap(&header.paletteType, sizeof(nzUInt16));
|
||||
NzByteSwap(&header.horzSize, sizeof(nzUInt16));
|
||||
NzByteSwap(&header.vertSize, sizeof(nzUInt16));
|
||||
#endif
|
||||
|
||||
unsigned int bitCount = header.bitsPerPixel * header.numColorPlanes;
|
||||
unsigned int width = header.xmax - header.xmin+1;
|
||||
unsigned int height = header.ymax - header.ymin+1;
|
||||
|
||||
if (!image->Create(nzImageType_2D, nzPixelFormat_RGB8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1))
|
||||
{
|
||||
NazaraError("Failed to create image");
|
||||
return false;
|
||||
}
|
||||
|
||||
nzUInt8* pixels = image->GetPixels();
|
||||
|
||||
int rle_value = 0;
|
||||
unsigned int rle_count = 0;
|
||||
|
||||
switch (bitCount)
|
||||
{
|
||||
case 1:
|
||||
UInt8 manufacturer;
|
||||
if (stream.Read(&manufacturer, 1) == 1)
|
||||
{
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
nzUInt8* ptr = &pixels[y * width * 3];
|
||||
int bytes = header.bytesPerScanLine;
|
||||
|
||||
/* decode line number y */
|
||||
while (bytes--)
|
||||
{
|
||||
if (rle_count == 0)
|
||||
{
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rle_value < 0xc0)
|
||||
rle_count = 1;
|
||||
else
|
||||
{
|
||||
rle_count = rle_value - 0xc0;
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rle_count--;
|
||||
|
||||
for (int i = 7; i >= 0; --i)
|
||||
{
|
||||
int colorIndex = ((rle_value & (1 << i)) > 0);
|
||||
|
||||
*ptr++ = header.palette[colorIndex * 3 + 0];
|
||||
*ptr++ = header.palette[colorIndex * 3 + 1];
|
||||
*ptr++ = header.palette[colorIndex * 3 + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
if (manufacturer == 0x0a)
|
||||
return Ternary_True;
|
||||
}
|
||||
|
||||
case 4:
|
||||
return Ternary_False;
|
||||
}
|
||||
|
||||
bool Load(Image* image, InputStream& stream, const ImageParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
pcx_header header;
|
||||
if (stream.Read(&header, sizeof(pcx_header)) != sizeof(pcx_header))
|
||||
{
|
||||
std::unique_ptr<nzUInt8[]> colorIndex(new nzUInt8[width]);
|
||||
std::unique_ptr<nzUInt8[]> line(new nzUInt8[header.bytesPerScanLine]);
|
||||
NazaraError("Failed to read header");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
// Les fichiers PCX sont en little endian
|
||||
SwapBytes(&header.xmin, sizeof(UInt16));
|
||||
SwapBytes(&header.ymin, sizeof(UInt16));
|
||||
SwapBytes(&header.xmax, sizeof(UInt16));
|
||||
SwapBytes(&header.ymax, sizeof(UInt16));
|
||||
SwapBytes(&header.horzRes, sizeof(UInt16));
|
||||
SwapBytes(&header.vertRes, sizeof(UInt16));
|
||||
|
||||
SwapBytes(&header.bytesPerScanLine, sizeof(UInt16));
|
||||
SwapBytes(&header.paletteType, sizeof(UInt16));
|
||||
SwapBytes(&header.horzSize, sizeof(UInt16));
|
||||
SwapBytes(&header.vertSize, sizeof(UInt16));
|
||||
#endif
|
||||
|
||||
unsigned int bitCount = header.bitsPerPixel * header.numColorPlanes;
|
||||
unsigned int width = header.xmax - header.xmin+1;
|
||||
unsigned int height = header.ymax - header.ymin+1;
|
||||
|
||||
if (!image->Create(ImageType_2D, PixelFormatType_RGB8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1))
|
||||
{
|
||||
NazaraError("Failed to create image");
|
||||
return false;
|
||||
}
|
||||
|
||||
UInt8* pixels = image->GetPixels();
|
||||
|
||||
int rle_value = 0;
|
||||
unsigned int rle_count = 0;
|
||||
|
||||
switch (bitCount)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
nzUInt8* ptr = &pixels[y * width * 3];
|
||||
|
||||
std::memset(colorIndex.get(), 0, width);
|
||||
|
||||
for (unsigned int c = 0; c < 4; ++c)
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
nzUInt8* pLine = line.get();
|
||||
UInt8* ptr = &pixels[y * width * 3];
|
||||
int bytes = header.bytesPerScanLine;
|
||||
|
||||
/* decode line number y */
|
||||
@@ -169,7 +116,7 @@ namespace
|
||||
{
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
|
||||
NazaraError("Failed to read stream (byte " + String::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -180,114 +127,125 @@ namespace
|
||||
rle_count = rle_value - 0xc0;
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
|
||||
NazaraError("Failed to read stream (byte " + String::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rle_count--;
|
||||
*(pLine++) = rle_value;
|
||||
|
||||
for (int i = 7; i >= 0; --i)
|
||||
{
|
||||
int colorIndex = ((rle_value & (1 << i)) > 0);
|
||||
|
||||
*ptr++ = header.palette[colorIndex * 3 + 0];
|
||||
*ptr++ = header.palette[colorIndex * 3 + 1];
|
||||
*ptr++ = header.palette[colorIndex * 3 + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
{
|
||||
std::unique_ptr<UInt8[]> colorIndex(new UInt8[width]);
|
||||
std::unique_ptr<UInt8[]> line(new UInt8[header.bytesPerScanLine]);
|
||||
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
UInt8* ptr = &pixels[y * width * 3];
|
||||
|
||||
std::memset(colorIndex.get(), 0, width);
|
||||
|
||||
for (unsigned int c = 0; c < 4; ++c)
|
||||
{
|
||||
UInt8* pLine = line.get();
|
||||
int bytes = header.bytesPerScanLine;
|
||||
|
||||
/* decode line number y */
|
||||
while (bytes--)
|
||||
{
|
||||
if (rle_count == 0)
|
||||
{
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + String::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rle_value < 0xc0)
|
||||
rle_count = 1;
|
||||
else
|
||||
{
|
||||
rle_count = rle_value - 0xc0;
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + String::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rle_count--;
|
||||
*(pLine++) = rle_value;
|
||||
}
|
||||
|
||||
/* compute line's color indexes */
|
||||
for (unsigned int x = 0; x < width; ++x)
|
||||
{
|
||||
if (line[x / 8] & (128 >> (x % 8)))
|
||||
colorIndex[x] += (1 << c);
|
||||
}
|
||||
}
|
||||
|
||||
/* compute line's color indexes */
|
||||
/* decode scan line. color index => rgb */
|
||||
for (unsigned int x = 0; x < width; ++x)
|
||||
{
|
||||
if (line[x / 8] & (128 >> (x % 8)))
|
||||
colorIndex[x] += (1 << c);
|
||||
*ptr++ = header.palette[colorIndex[x] * 3 + 0];
|
||||
*ptr++ = header.palette[colorIndex[x] * 3 + 1];
|
||||
*ptr++ = header.palette[colorIndex[x] * 3 + 2];
|
||||
}
|
||||
}
|
||||
|
||||
/* decode scan line. color index => rgb */
|
||||
for (unsigned int x = 0; x < width; ++x)
|
||||
break;
|
||||
}
|
||||
|
||||
case 8:
|
||||
{
|
||||
UInt8 palette[768];
|
||||
|
||||
/* the palette is contained in the last 769 bytes of the file */
|
||||
UInt64 curPos = stream.GetCursorPos();
|
||||
stream.SetCursorPos(stream.GetSize()-769);
|
||||
UInt8 magic;
|
||||
if (!stream.Read(&magic, 1))
|
||||
{
|
||||
*ptr++ = header.palette[colorIndex[x] * 3 + 0];
|
||||
*ptr++ = header.palette[colorIndex[x] * 3 + 1];
|
||||
*ptr++ = header.palette[colorIndex[x] * 3 + 2];
|
||||
NazaraError("Failed to read stream (byte " + String::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 8:
|
||||
{
|
||||
nzUInt8 palette[768];
|
||||
|
||||
/* the palette is contained in the last 769 bytes of the file */
|
||||
nzUInt64 curPos = stream.GetCursorPos();
|
||||
stream.SetCursorPos(stream.GetSize()-769);
|
||||
nzUInt8 magic;
|
||||
if (!stream.Read(&magic, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
/* first byte must be equal to 0x0c (12) */
|
||||
if (magic != 0x0c)
|
||||
{
|
||||
NazaraError("Colormap's first byte must be 0x0c (0x" + NzString::Number(magic, 16) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
/* read palette */
|
||||
if (stream.Read(palette, 768) != 768)
|
||||
{
|
||||
NazaraError("Failed to read palette");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.SetCursorPos(curPos);
|
||||
|
||||
/* read pixel data */
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
nzUInt8* ptr = &pixels[y * width * 3];
|
||||
int bytes = header.bytesPerScanLine;
|
||||
|
||||
/* decode line number y */
|
||||
while (bytes--)
|
||||
/* first byte must be equal to 0x0c (12) */
|
||||
if (magic != 0x0c)
|
||||
{
|
||||
if (rle_count == 0)
|
||||
{
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rle_value < 0xc0)
|
||||
rle_count = 1;
|
||||
else
|
||||
{
|
||||
rle_count = rle_value - 0xc0;
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rle_count--;
|
||||
|
||||
*ptr++ = palette[rle_value * 3 + 0];
|
||||
*ptr++ = palette[rle_value * 3 + 1];
|
||||
*ptr++ = palette[rle_value * 3 + 2];
|
||||
NazaraError("Colormap's first byte must be 0x0c (0x" + String::Number(magic, 16) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 24:
|
||||
{
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
/* for each color plane */
|
||||
for (int c = 0; c < 3; ++c)
|
||||
/* read palette */
|
||||
if (stream.Read(palette, 768) != 768)
|
||||
{
|
||||
nzUInt8* ptr = &pixels[y * width * 3];
|
||||
NazaraError("Failed to read palette");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.SetCursorPos(curPos);
|
||||
|
||||
/* read pixel data */
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
UInt8* ptr = &pixels[y * width * 3];
|
||||
int bytes = header.bytesPerScanLine;
|
||||
|
||||
/* decode line number y */
|
||||
@@ -297,7 +255,7 @@ namespace
|
||||
{
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
|
||||
NazaraError("Failed to read stream (byte " + String::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -308,39 +266,87 @@ namespace
|
||||
rle_count = rle_value - 0xc0;
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
|
||||
NazaraError("Failed to read stream (byte " + String::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rle_count--;
|
||||
ptr[c] = static_cast<nzUInt8>(rle_value);
|
||||
ptr += 3;
|
||||
|
||||
*ptr++ = palette[rle_value * 3 + 0];
|
||||
*ptr++ = palette[rle_value * 3 + 1];
|
||||
*ptr++ = palette[rle_value * 3 + 2];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 24:
|
||||
{
|
||||
for (unsigned int y = 0; y < height; ++y)
|
||||
{
|
||||
/* for each color plane */
|
||||
for (int c = 0; c < 3; ++c)
|
||||
{
|
||||
UInt8* ptr = &pixels[y * width * 3];
|
||||
int bytes = header.bytesPerScanLine;
|
||||
|
||||
/* decode line number y */
|
||||
while (bytes--)
|
||||
{
|
||||
if (rle_count == 0)
|
||||
{
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + String::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rle_value < 0xc0)
|
||||
rle_count = 1;
|
||||
else
|
||||
{
|
||||
rle_count = rle_value - 0xc0;
|
||||
if (!stream.Read(&rle_value, 1))
|
||||
{
|
||||
NazaraError("Failed to read stream (byte " + String::Number(stream.GetCursorPos()) + ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rle_count--;
|
||||
ptr[c] = static_cast<UInt8>(rle_value);
|
||||
ptr += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
NazaraError("Unsupported " + String::Number(bitCount) + " bitcount for pcx files");
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
NazaraError("Unsupported " + NzString::Number(bitCount) + " bitcount for pcx files");
|
||||
return false;
|
||||
if (parameters.loadFormat != PixelFormatType_Undefined)
|
||||
image->Convert(parameters.loadFormat);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterPCX()
|
||||
{
|
||||
ImageLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
if (parameters.loadFormat != nzPixelFormat_Undefined)
|
||||
image->Convert(parameters.loadFormat);
|
||||
|
||||
return true;
|
||||
void UnregisterPCX()
|
||||
{
|
||||
ImageLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_PCX_Register()
|
||||
{
|
||||
NzImageLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_PCX_Unregister()
|
||||
{
|
||||
NzImageLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_PCX_Register();
|
||||
void NzLoaders_PCX_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterPCX();
|
||||
void UnregisterPCX();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_PCX_HPP
|
||||
|
||||
@@ -13,82 +13,88 @@
|
||||
#include <set>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
int Read(void* userdata, char* data, int size)
|
||||
namespace
|
||||
{
|
||||
NzInputStream* stream = static_cast<NzInputStream*>(userdata);
|
||||
return static_cast<int>(stream->Read(data, size));
|
||||
}
|
||||
|
||||
void Skip(void* userdata, int size)
|
||||
{
|
||||
NzInputStream* stream = static_cast<NzInputStream*>(userdata);
|
||||
stream->SetCursorPos(static_cast<nzInt64>(stream->GetCursorPos()) + static_cast<nzInt64>(size));
|
||||
}
|
||||
|
||||
int Eof(void* userdata)
|
||||
{
|
||||
NzInputStream* stream = static_cast<NzInputStream*>(userdata);
|
||||
return stream->GetCursorPos() >= stream->GetSize();
|
||||
}
|
||||
|
||||
static stbi_io_callbacks callbacks = {Read, Skip, Eof};
|
||||
|
||||
bool IsSupported(const NzString& extension)
|
||||
{
|
||||
static std::set<NzString> supportedExtensions = {"bmp", "gif", "hdr", "jpg", "jpeg", "pic", "png", "ppm", "pgm", "psd", "tga"};
|
||||
return supportedExtensions.find(extension) != supportedExtensions.end();
|
||||
}
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzImageParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
int width, height, bpp;
|
||||
if (stbi_info_from_callbacks(&callbacks, &stream, &width, &height, &bpp))
|
||||
return nzTernary_True;
|
||||
else
|
||||
return nzTernary_False;
|
||||
}
|
||||
|
||||
bool Load(NzImage* image, NzInputStream& stream, const NzImageParams& parameters)
|
||||
{
|
||||
// Je charge tout en RGBA8 et je converti ensuite via la méthode Convert
|
||||
// Ceci à cause d'un bug de STB lorsqu'il s'agit de charger certaines images (ex: JPG) en "default"
|
||||
|
||||
int width, height, bpp;
|
||||
nzUInt8* ptr = stbi_load_from_callbacks(&callbacks, &stream, &width, &height, &bpp, STBI_rgb_alpha);
|
||||
if (!ptr)
|
||||
int Read(void* userdata, char* data, int size)
|
||||
{
|
||||
NazaraError("Failed to load image: " + NzString(stbi_failure_reason()));
|
||||
return false;
|
||||
InputStream* stream = static_cast<InputStream*>(userdata);
|
||||
return static_cast<int>(stream->Read(data, size));
|
||||
}
|
||||
|
||||
if (!image->Create(nzImageType_2D, nzPixelFormat_RGBA8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1))
|
||||
void Skip(void* userdata, int size)
|
||||
{
|
||||
NazaraError("Failed to create image");
|
||||
InputStream* stream = static_cast<InputStream*>(userdata);
|
||||
stream->SetCursorPos(static_cast<Int64>(stream->GetCursorPos()) + static_cast<Int64>(size));
|
||||
}
|
||||
|
||||
int Eof(void* userdata)
|
||||
{
|
||||
InputStream* stream = static_cast<InputStream*>(userdata);
|
||||
return stream->GetCursorPos() >= stream->GetSize();
|
||||
}
|
||||
|
||||
static stbi_io_callbacks callbacks = {Read, Skip, Eof};
|
||||
|
||||
bool IsSupported(const String& extension)
|
||||
{
|
||||
static std::set<String> supportedExtensions = {"bmp", "gif", "hdr", "jpg", "jpeg", "pic", "png", "ppm", "pgm", "psd", "tga"};
|
||||
return supportedExtensions.find(extension) != supportedExtensions.end();
|
||||
}
|
||||
|
||||
Ternary Check(InputStream& stream, const ImageParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
int width, height, bpp;
|
||||
if (stbi_info_from_callbacks(&callbacks, &stream, &width, &height, &bpp))
|
||||
return Ternary_True;
|
||||
else
|
||||
return Ternary_False;
|
||||
}
|
||||
|
||||
bool Load(Image* image, InputStream& stream, const ImageParams& parameters)
|
||||
{
|
||||
// Je charge tout en RGBA8 et je converti ensuite via la méthode Convert
|
||||
// Ceci à cause d'un bug de STB lorsqu'il s'agit de charger certaines images (ex: JPG) en "default"
|
||||
|
||||
int width, height, bpp;
|
||||
UInt8* ptr = stbi_load_from_callbacks(&callbacks, &stream, &width, &height, &bpp, STBI_rgb_alpha);
|
||||
if (!ptr)
|
||||
{
|
||||
NazaraError("Failed to load image: " + String(stbi_failure_reason()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!image->Create(ImageType_2D, PixelFormatType_RGBA8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1))
|
||||
{
|
||||
NazaraError("Failed to create image");
|
||||
stbi_image_free(ptr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
image->Update(ptr);
|
||||
stbi_image_free(ptr);
|
||||
|
||||
return false;
|
||||
if (parameters.loadFormat != PixelFormatType_Undefined)
|
||||
image->Convert(parameters.loadFormat);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterSTB()
|
||||
{
|
||||
ImageLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
image->Update(ptr);
|
||||
stbi_image_free(ptr);
|
||||
|
||||
if (parameters.loadFormat != nzPixelFormat_Undefined)
|
||||
image->Convert(parameters.loadFormat);
|
||||
|
||||
return true;
|
||||
void UnregisterSTB()
|
||||
{
|
||||
ImageLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_STB_Register()
|
||||
{
|
||||
NzImageLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_STB_Unregister()
|
||||
{
|
||||
NzImageLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_STB_Register();
|
||||
void NzLoaders_STB_Unregister();
|
||||
namespace Nz
|
||||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterSTB();
|
||||
void UnregisterSTB();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_STB_HPP
|
||||
|
||||
@@ -6,259 +6,262 @@
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
const unsigned int s_atlasStartSize = 512;
|
||||
}
|
||||
|
||||
NzGuillotineImageAtlas::NzGuillotineImageAtlas() :
|
||||
m_rectChoiceHeuristic(NzGuillotineBinPack::RectBestAreaFit),
|
||||
m_rectSplitHeuristic(NzGuillotineBinPack::SplitMinimizeArea)
|
||||
{
|
||||
}
|
||||
|
||||
NzGuillotineImageAtlas::~NzGuillotineImageAtlas() = default;
|
||||
|
||||
void NzGuillotineImageAtlas::Clear()
|
||||
{
|
||||
m_layers.clear();
|
||||
OnAtlasCleared(this);
|
||||
}
|
||||
|
||||
void NzGuillotineImageAtlas::Free(NzSparsePtr<const NzRectui> rects, NzSparsePtr<unsigned int> layers, unsigned int count)
|
||||
{
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
namespace
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (layers[i] >= m_layers.size())
|
||||
const unsigned int s_atlasStartSize = 512;
|
||||
}
|
||||
|
||||
GuillotineImageAtlas::GuillotineImageAtlas() :
|
||||
m_rectChoiceHeuristic(GuillotineBinPack::RectBestAreaFit),
|
||||
m_rectSplitHeuristic(GuillotineBinPack::SplitMinimizeArea)
|
||||
{
|
||||
}
|
||||
|
||||
GuillotineImageAtlas::~GuillotineImageAtlas() = default;
|
||||
|
||||
void GuillotineImageAtlas::Clear()
|
||||
{
|
||||
m_layers.clear();
|
||||
OnAtlasCleared(this);
|
||||
}
|
||||
|
||||
void GuillotineImageAtlas::Free(SparsePtr<const Rectui> rects, SparsePtr<unsigned int> layers, unsigned int count)
|
||||
{
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
NazaraWarning("Rectangle #" + NzString::Number(i) + " belong to an out-of-bounds layer (" + NzString::Number(i) + " >= " + NzString::Number(m_layers.size()) + ")");
|
||||
continue;
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (layers[i] >= m_layers.size())
|
||||
{
|
||||
NazaraWarning("Rectangle #" + String::Number(i) + " belong to an out-of-bounds layer (" + String::Number(i) + " >= " + String::Number(m_layers.size()) + ")");
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_layers[layers[i]].binPack.FreeRectangle(rects[i]);
|
||||
m_layers[layers[i]].freedRectangles++;
|
||||
}
|
||||
}
|
||||
|
||||
GuillotineBinPack::FreeRectChoiceHeuristic GuillotineImageAtlas::GetRectChoiceHeuristic() const
|
||||
{
|
||||
return m_rectChoiceHeuristic;
|
||||
}
|
||||
|
||||
GuillotineBinPack::GuillotineSplitHeuristic GuillotineImageAtlas::GetRectSplitHeuristic() const
|
||||
{
|
||||
return m_rectSplitHeuristic;
|
||||
}
|
||||
|
||||
AbstractImage* GuillotineImageAtlas::GetLayer(unsigned int layerIndex) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (layerIndex >= m_layers.size())
|
||||
{
|
||||
NazaraError("Layer index out of range (" + String::Number(layerIndex) + " >= " + String::Number(m_layers.size()) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_layers[layers[i]].binPack.FreeRectangle(rects[i]);
|
||||
m_layers[layers[i]].freedRectangles++;
|
||||
Layer& layer = m_layers[layerIndex];
|
||||
ProcessGlyphQueue(layer);
|
||||
|
||||
return layer.image.get();
|
||||
}
|
||||
}
|
||||
|
||||
NzGuillotineBinPack::FreeRectChoiceHeuristic NzGuillotineImageAtlas::GetRectChoiceHeuristic() const
|
||||
{
|
||||
return m_rectChoiceHeuristic;
|
||||
}
|
||||
|
||||
NzGuillotineBinPack::GuillotineSplitHeuristic NzGuillotineImageAtlas::GetRectSplitHeuristic() const
|
||||
{
|
||||
return m_rectSplitHeuristic;
|
||||
}
|
||||
|
||||
NzAbstractImage* NzGuillotineImageAtlas::GetLayer(unsigned int layerIndex) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (layerIndex >= m_layers.size())
|
||||
unsigned int GuillotineImageAtlas::GetLayerCount() const
|
||||
{
|
||||
NazaraError("Layer index out of range (" + NzString::Number(layerIndex) + " >= " + NzString::Number(m_layers.size()) + ')');
|
||||
return nullptr;
|
||||
return m_layers.size();
|
||||
}
|
||||
#endif
|
||||
|
||||
Layer& layer = m_layers[layerIndex];
|
||||
ProcessGlyphQueue(layer);
|
||||
|
||||
return layer.image.get();
|
||||
}
|
||||
|
||||
unsigned int NzGuillotineImageAtlas::GetLayerCount() const
|
||||
{
|
||||
return m_layers.size();
|
||||
}
|
||||
|
||||
nzUInt32 NzGuillotineImageAtlas::GetStorage() const
|
||||
{
|
||||
return nzDataStorage_Software;
|
||||
}
|
||||
|
||||
bool NzGuillotineImageAtlas::Insert(const NzImage& image, NzRectui* rect, bool* flipped, unsigned int* layerIndex)
|
||||
{
|
||||
if (m_layers.empty())
|
||||
// On créé une première couche s'il n'y en a pas
|
||||
m_layers.resize(1);
|
||||
|
||||
// Cette fonction ne fait qu'insérer un rectangle de façon virtuelle, l'insertion des images se fait après
|
||||
for (unsigned int i = 0; i < m_layers.size(); ++i)
|
||||
UInt32 GuillotineImageAtlas::GetStorage() const
|
||||
{
|
||||
Layer& layer = m_layers[i];
|
||||
return DataStorage_Software;
|
||||
}
|
||||
|
||||
// Une fois qu'un certain nombre de rectangles ont étés libérés d'une couche, on fusionne les rectangles libres
|
||||
if (layer.freedRectangles > 10) // Valeur totalement arbitraire
|
||||
bool GuillotineImageAtlas::Insert(const Image& image, Rectui* rect, bool* flipped, unsigned int* layerIndex)
|
||||
{
|
||||
if (m_layers.empty())
|
||||
// On créé une première couche s'il n'y en a pas
|
||||
m_layers.resize(1);
|
||||
|
||||
// Cette fonction ne fait qu'insérer un rectangle de façon virtuelle, l'insertion des images se fait après
|
||||
for (unsigned int i = 0; i < m_layers.size(); ++i)
|
||||
{
|
||||
while (layer.binPack.MergeFreeRectangles()); // Tant qu'une fusion est possible
|
||||
layer.freedRectangles = 0; // Et on repart de zéro
|
||||
Layer& layer = m_layers[i];
|
||||
|
||||
// Une fois qu'un certain nombre de rectangles ont étés libérés d'une couche, on fusionne les rectangles libres
|
||||
if (layer.freedRectangles > 10) // Valeur totalement arbitraire
|
||||
{
|
||||
while (layer.binPack.MergeFreeRectangles()); // Tant qu'une fusion est possible
|
||||
layer.freedRectangles = 0; // Et on repart de zéro
|
||||
}
|
||||
|
||||
if (layer.binPack.Insert(rect, flipped, 1, false, m_rectChoiceHeuristic, m_rectSplitHeuristic))
|
||||
{
|
||||
// Insertion réussie dans l'une des couches, on place le glyphe en file d'attente
|
||||
layer.queuedGlyphs.resize(layer.queuedGlyphs.size()+1);
|
||||
QueuedGlyph& glyph = layer.queuedGlyphs.back();
|
||||
glyph.flipped = *flipped;
|
||||
glyph.image = image; // Merci le Copy-On-Write
|
||||
glyph.rect = *rect;
|
||||
|
||||
*layerIndex = i;
|
||||
return true;
|
||||
}
|
||||
else if (i == m_layers.size() - 1) // Dernière itération ?
|
||||
{
|
||||
// Dernière couche, et le glyphe ne rentre pas, peut-on agrandir la taille de l'image ?
|
||||
Vector2ui newSize = layer.binPack.GetSize()*2;
|
||||
if (newSize == Vector2ui::Zero())
|
||||
newSize.Set(s_atlasStartSize);
|
||||
|
||||
if (ResizeLayer(layer, newSize))
|
||||
{
|
||||
// Oui on peut !
|
||||
layer.binPack.Expand(newSize); // On ajuste l'atlas virtuel
|
||||
|
||||
// Et on relance la boucle sur la nouvelle dernière couche
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// On ne peut plus agrandir la dernière couche, il est temps d'en créer une nouvelle
|
||||
newSize.Set(s_atlasStartSize);
|
||||
|
||||
Layer newLayer;
|
||||
if (!ResizeLayer(newLayer, newSize))
|
||||
{
|
||||
// Impossible d'allouer une nouvelle couche, nous manquons probablement de mémoire (ou le glyphe est trop grand)
|
||||
NazaraError("Failed to allocate new layer, we are probably out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
newLayer.binPack.Reset(newSize);
|
||||
|
||||
m_layers.emplace_back(std::move(newLayer)); // Insertion du layer
|
||||
|
||||
// On laisse la boucle insérer toute seule le rectangle à la prochaine itération
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layer.binPack.Insert(rect, flipped, 1, false, m_rectChoiceHeuristic, m_rectSplitHeuristic))
|
||||
{
|
||||
// Insertion réussie dans l'une des couches, on place le glyphe en file d'attente
|
||||
layer.queuedGlyphs.resize(layer.queuedGlyphs.size()+1);
|
||||
QueuedGlyph& glyph = layer.queuedGlyphs.back();
|
||||
glyph.flipped = *flipped;
|
||||
glyph.image = image; // Merci le Copy-On-Write
|
||||
glyph.rect = *rect;
|
||||
NazaraInternalError("Unknown error"); // Normalement on ne peut pas arriver ici
|
||||
return false;
|
||||
}
|
||||
|
||||
*layerIndex = i;
|
||||
void GuillotineImageAtlas::SetRectChoiceHeuristic(GuillotineBinPack::FreeRectChoiceHeuristic heuristic)
|
||||
{
|
||||
m_rectChoiceHeuristic = heuristic;
|
||||
}
|
||||
|
||||
void GuillotineImageAtlas::SetRectSplitHeuristic(GuillotineBinPack::GuillotineSplitHeuristic heuristic)
|
||||
{
|
||||
m_rectSplitHeuristic = heuristic;
|
||||
}
|
||||
|
||||
AbstractImage* GuillotineImageAtlas::ResizeImage(AbstractImage* oldImage, const Vector2ui& size) const
|
||||
{
|
||||
std::unique_ptr<Image> newImage(new Image(ImageType_2D, PixelFormatType_A8, size.x, size.y));
|
||||
if (oldImage)
|
||||
{
|
||||
Image& image = *static_cast<Image*>(oldImage);
|
||||
newImage->Copy(image, Rectui(size), Vector2ui(0, 0)); // Copie des anciennes données
|
||||
}
|
||||
|
||||
return newImage.release();
|
||||
}
|
||||
|
||||
bool GuillotineImageAtlas::ResizeLayer(Layer& layer, const Vector2ui& size)
|
||||
{
|
||||
AbstractImage* oldLayer = layer.image.get();
|
||||
|
||||
std::unique_ptr<AbstractImage> newImage(ResizeImage(layer.image.get(), size));
|
||||
if (!newImage)
|
||||
return false; // Nous n'avons pas pu allouer
|
||||
|
||||
if (newImage.get() == oldLayer) // Le layer a été agrandi dans le même objet, pas de souci
|
||||
{
|
||||
newImage.release(); // On possède déjà un unique_ptr sur cette ressource
|
||||
return true;
|
||||
}
|
||||
else if (i == m_layers.size() - 1) // Dernière itération ?
|
||||
{
|
||||
// Dernière couche, et le glyphe ne rentre pas, peut-on agrandir la taille de l'image ?
|
||||
NzVector2ui newSize = layer.binPack.GetSize()*2;
|
||||
if (newSize == NzVector2ui::Zero())
|
||||
newSize.Set(s_atlasStartSize);
|
||||
|
||||
if (ResizeLayer(layer, newSize))
|
||||
{
|
||||
// Oui on peut !
|
||||
layer.binPack.Expand(newSize); // On ajuste l'atlas virtuel
|
||||
// On indique à ceux que ça intéresse qu'on a changé de pointeur
|
||||
// (chose très importante pour ceux qui le stockent)
|
||||
OnAtlasLayerChange(this, layer.image.get(), newImage.get());
|
||||
|
||||
// Et on relance la boucle sur la nouvelle dernière couche
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// On ne peut plus agrandir la dernière couche, il est temps d'en créer une nouvelle
|
||||
newSize.Set(s_atlasStartSize);
|
||||
// Et on ne met à jour le pointeur qu'après (car cette ligne libère également l'ancienne image)
|
||||
layer.image = std::move(newImage);
|
||||
|
||||
Layer newLayer;
|
||||
if (!ResizeLayer(newLayer, newSize))
|
||||
{
|
||||
// Impossible d'allouer une nouvelle couche, nous manquons probablement de mémoire (ou le glyphe est trop grand)
|
||||
NazaraError("Failed to allocate new layer, we are probably out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
newLayer.binPack.Reset(newSize);
|
||||
|
||||
m_layers.emplace_back(std::move(newLayer)); // Insertion du layer
|
||||
|
||||
// On laisse la boucle insérer toute seule le rectangle à la prochaine itération
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NazaraInternalError("Unknown error"); // Normalement on ne peut pas arriver ici
|
||||
return false;
|
||||
}
|
||||
|
||||
void NzGuillotineImageAtlas::SetRectChoiceHeuristic(NzGuillotineBinPack::FreeRectChoiceHeuristic heuristic)
|
||||
{
|
||||
m_rectChoiceHeuristic = heuristic;
|
||||
}
|
||||
|
||||
void NzGuillotineImageAtlas::SetRectSplitHeuristic(NzGuillotineBinPack::GuillotineSplitHeuristic heuristic)
|
||||
{
|
||||
m_rectSplitHeuristic = heuristic;
|
||||
}
|
||||
|
||||
NzAbstractImage* NzGuillotineImageAtlas::ResizeImage(NzAbstractImage* oldImage, const NzVector2ui& size) const
|
||||
{
|
||||
std::unique_ptr<NzImage> newImage(new NzImage(nzImageType_2D, nzPixelFormat_A8, size.x, size.y));
|
||||
if (oldImage)
|
||||
{
|
||||
NzImage& image = *static_cast<NzImage*>(oldImage);
|
||||
newImage->Copy(image, NzRectui(size), NzVector2ui(0, 0)); // Copie des anciennes données
|
||||
}
|
||||
|
||||
return newImage.release();
|
||||
}
|
||||
|
||||
bool NzGuillotineImageAtlas::ResizeLayer(Layer& layer, const NzVector2ui& size)
|
||||
{
|
||||
NzAbstractImage* oldLayer = layer.image.get();
|
||||
|
||||
std::unique_ptr<NzAbstractImage> newImage(ResizeImage(layer.image.get(), size));
|
||||
if (!newImage)
|
||||
return false; // Nous n'avons pas pu allouer
|
||||
|
||||
if (newImage.get() == oldLayer) // Le layer a été agrandi dans le même objet, pas de souci
|
||||
{
|
||||
newImage.release(); // On possède déjà un unique_ptr sur cette ressource
|
||||
return true;
|
||||
}
|
||||
|
||||
// On indique à ceux que ça intéresse qu'on a changé de pointeur
|
||||
// (chose très importante pour ceux qui le stockent)
|
||||
OnAtlasLayerChange(this, layer.image.get(), newImage.get());
|
||||
|
||||
// Et on ne met à jour le pointeur qu'après (car cette ligne libère également l'ancienne image)
|
||||
layer.image = std::move(newImage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzGuillotineImageAtlas::ProcessGlyphQueue(Layer& layer) const
|
||||
{
|
||||
std::vector<nzUInt8> pixelBuffer;
|
||||
|
||||
for (QueuedGlyph& glyph : layer.queuedGlyphs)
|
||||
void GuillotineImageAtlas::ProcessGlyphQueue(Layer& layer) const
|
||||
{
|
||||
unsigned int glyphWidth = glyph.image.GetWidth();
|
||||
unsigned int glyphHeight = glyph.image.GetHeight();
|
||||
std::vector<UInt8> pixelBuffer;
|
||||
|
||||
// Calcul de l'éventuel padding (pixels de contour)
|
||||
unsigned int paddingX;
|
||||
unsigned int paddingY;
|
||||
if (glyph.flipped)
|
||||
for (QueuedGlyph& glyph : layer.queuedGlyphs)
|
||||
{
|
||||
paddingX = (glyph.rect.height - glyphWidth)/2;
|
||||
paddingY = (glyph.rect.width - glyphHeight)/2;
|
||||
}
|
||||
else
|
||||
{
|
||||
paddingX = (glyph.rect.width - glyphWidth)/2;
|
||||
paddingY = (glyph.rect.height - glyphHeight)/2;
|
||||
}
|
||||
unsigned int glyphWidth = glyph.image.GetWidth();
|
||||
unsigned int glyphHeight = glyph.image.GetHeight();
|
||||
|
||||
if (paddingX > 0 || paddingY > 0)
|
||||
{
|
||||
// On remplit les contours
|
||||
pixelBuffer.resize(glyph.rect.width * glyph.rect.height);
|
||||
std::memset(pixelBuffer.data(), 0, glyph.rect.width*glyph.rect.height*sizeof(nzUInt8));
|
||||
|
||||
layer.image->Update(pixelBuffer.data(), glyph.rect);
|
||||
}
|
||||
|
||||
const nzUInt8* pixels;
|
||||
// On copie le glyphe dans l'atlas
|
||||
if (glyph.flipped)
|
||||
{
|
||||
pixelBuffer.resize(glyphHeight * glyphWidth);
|
||||
|
||||
// On tourne le glyphe pour qu'il rentre dans le rectangle
|
||||
const nzUInt8* src = glyph.image.GetConstPixels();
|
||||
nzUInt8* ptr = pixelBuffer.data();
|
||||
|
||||
unsigned int lineStride = glyphWidth*sizeof(nzUInt8); // BPP = 1
|
||||
src += lineStride-1; // Départ en haut à droite
|
||||
for (unsigned int x = 0; x < glyphWidth; ++x)
|
||||
// Calcul de l'éventuel padding (pixels de contour)
|
||||
unsigned int paddingX;
|
||||
unsigned int paddingY;
|
||||
if (glyph.flipped)
|
||||
{
|
||||
for (unsigned int y = 0; y < glyphHeight; ++y)
|
||||
{
|
||||
*ptr++ = *src;
|
||||
src += lineStride;
|
||||
}
|
||||
|
||||
src -= glyphHeight*lineStride + 1;
|
||||
paddingX = (glyph.rect.height - glyphWidth)/2;
|
||||
paddingY = (glyph.rect.width - glyphHeight)/2;
|
||||
}
|
||||
else
|
||||
{
|
||||
paddingX = (glyph.rect.width - glyphWidth)/2;
|
||||
paddingY = (glyph.rect.height - glyphHeight)/2;
|
||||
}
|
||||
|
||||
pixels = pixelBuffer.data();
|
||||
std::swap(glyphWidth, glyphHeight);
|
||||
if (paddingX > 0 || paddingY > 0)
|
||||
{
|
||||
// On remplit les contours
|
||||
pixelBuffer.resize(glyph.rect.width * glyph.rect.height);
|
||||
std::memset(pixelBuffer.data(), 0, glyph.rect.width*glyph.rect.height*sizeof(UInt8));
|
||||
|
||||
layer.image->Update(pixelBuffer.data(), glyph.rect);
|
||||
}
|
||||
|
||||
const UInt8* pixels;
|
||||
// On copie le glyphe dans l'atlas
|
||||
if (glyph.flipped)
|
||||
{
|
||||
pixelBuffer.resize(glyphHeight * glyphWidth);
|
||||
|
||||
// On tourne le glyphe pour qu'il rentre dans le rectangle
|
||||
const UInt8* src = glyph.image.GetConstPixels();
|
||||
UInt8* ptr = pixelBuffer.data();
|
||||
|
||||
unsigned int lineStride = glyphWidth*sizeof(UInt8); // BPP = 1
|
||||
src += lineStride-1; // Départ en haut à droite
|
||||
for (unsigned int x = 0; x < glyphWidth; ++x)
|
||||
{
|
||||
for (unsigned int y = 0; y < glyphHeight; ++y)
|
||||
{
|
||||
*ptr++ = *src;
|
||||
src += lineStride;
|
||||
}
|
||||
|
||||
src -= glyphHeight*lineStride + 1;
|
||||
}
|
||||
|
||||
pixels = pixelBuffer.data();
|
||||
std::swap(glyphWidth, glyphHeight);
|
||||
}
|
||||
else
|
||||
pixels = glyph.image.GetConstPixels();
|
||||
|
||||
layer.image->Update(pixels, Rectui(glyph.rect.x + paddingX, glyph.rect.y + paddingY, glyphWidth, glyphHeight), 0, glyphWidth, glyphHeight);
|
||||
glyph.image.Destroy(); // On libère l'image dès que possible (pour réduire la consommation)
|
||||
}
|
||||
else
|
||||
pixels = glyph.image.GetConstPixels();
|
||||
|
||||
layer.image->Update(pixels, NzRectui(glyph.rect.x + paddingX, glyph.rect.y + paddingY, glyphWidth, glyphHeight), 0, glyphWidth, glyphHeight);
|
||||
glyph.image.Destroy(); // On libère l'image dès que possible (pour réduire la consommation)
|
||||
layer.queuedGlyphs.clear();
|
||||
}
|
||||
|
||||
layer.queuedGlyphs.clear();
|
||||
}
|
||||
|
||||
@@ -14,45 +14,48 @@
|
||||
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzIcon::NzIcon() :
|
||||
m_impl(nullptr)
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
NzIcon::~NzIcon()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool NzIcon::Create(const NzImage& icon)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
m_impl = new NzIconImpl;
|
||||
if (!m_impl->Create(icon))
|
||||
Icon::Icon() :
|
||||
m_impl(nullptr)
|
||||
{
|
||||
NazaraError("Failed to create icon implementation");
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzIcon::Destroy()
|
||||
{
|
||||
if (m_impl)
|
||||
Icon::~Icon()
|
||||
{
|
||||
m_impl->Destroy();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
bool Icon::Create(const Image& icon)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
m_impl = new IconImpl;
|
||||
if (!m_impl->Create(icon))
|
||||
{
|
||||
NazaraError("Failed to create icon implementation");
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Icon::Destroy()
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
m_impl->Destroy();
|
||||
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Icon::IsValid() const
|
||||
{
|
||||
return m_impl != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool NzIcon::IsValid() const
|
||||
{
|
||||
return m_impl != nullptr;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,238 +12,241 @@
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzIndexBuffer::NzIndexBuffer(bool largeIndices, NzBuffer* buffer)
|
||||
namespace Nz
|
||||
{
|
||||
NzErrorFlags(nzErrorFlag_ThrowException, true);
|
||||
Reset(largeIndices, buffer);
|
||||
}
|
||||
|
||||
NzIndexBuffer::NzIndexBuffer(bool largeIndices, NzBuffer* buffer, unsigned int startOffset, unsigned int endOffset)
|
||||
{
|
||||
NzErrorFlags(nzErrorFlag_ThrowException, true);
|
||||
Reset(largeIndices, buffer, startOffset, endOffset);
|
||||
}
|
||||
|
||||
NzIndexBuffer::NzIndexBuffer(bool largeIndices, unsigned int length, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
NzErrorFlags(nzErrorFlag_ThrowException, true);
|
||||
Reset(largeIndices, length, storage, usage);
|
||||
}
|
||||
|
||||
NzIndexBuffer::NzIndexBuffer(const NzIndexBuffer& indexBuffer) :
|
||||
NzRefCounted(),
|
||||
m_buffer(indexBuffer.m_buffer),
|
||||
m_largeIndices(indexBuffer.m_largeIndices),
|
||||
m_endOffset(indexBuffer.m_endOffset),
|
||||
m_indexCount(indexBuffer.m_indexCount),
|
||||
m_startOffset(indexBuffer.m_startOffset)
|
||||
{
|
||||
}
|
||||
|
||||
NzIndexBuffer::~NzIndexBuffer()
|
||||
{
|
||||
OnIndexBufferRelease(this);
|
||||
}
|
||||
|
||||
unsigned int NzIndexBuffer::ComputeCacheMissCount() const
|
||||
{
|
||||
NzIndexMapper mapper(this);
|
||||
|
||||
return NzComputeCacheMissCount(mapper.begin(), m_indexCount);
|
||||
}
|
||||
|
||||
bool NzIndexBuffer::Fill(const void* data, unsigned int startIndex, unsigned int length, bool forceDiscard)
|
||||
{
|
||||
unsigned int stride = GetStride();
|
||||
return FillRaw(data, startIndex*stride, length*stride, forceDiscard);
|
||||
}
|
||||
|
||||
bool NzIndexBuffer::FillRaw(const void* data, unsigned int offset, unsigned int size, bool forceDiscard)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
IndexBuffer::IndexBuffer(bool largeIndices, Buffer* buffer)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Fill(data, m_startOffset+offset, size, forceDiscard);
|
||||
}
|
||||
|
||||
NzBuffer* NzIndexBuffer::GetBuffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
unsigned int NzIndexBuffer::GetEndOffset() const
|
||||
{
|
||||
return m_endOffset;
|
||||
}
|
||||
|
||||
unsigned int NzIndexBuffer::GetIndexCount() const
|
||||
{
|
||||
return m_indexCount;
|
||||
}
|
||||
|
||||
unsigned int NzIndexBuffer::GetStride() const
|
||||
{
|
||||
return (m_largeIndices) ? sizeof(nzUInt32) : sizeof(nzUInt16);
|
||||
}
|
||||
|
||||
unsigned int NzIndexBuffer::GetStartOffset() const
|
||||
{
|
||||
return m_startOffset;
|
||||
}
|
||||
|
||||
bool NzIndexBuffer::HasLargeIndices() const
|
||||
{
|
||||
return m_largeIndices;
|
||||
}
|
||||
|
||||
bool NzIndexBuffer::IsHardware() const
|
||||
{
|
||||
return m_buffer->IsHardware();
|
||||
}
|
||||
|
||||
bool NzIndexBuffer::IsValid() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
void* NzIndexBuffer::Map(nzBufferAccess access, unsigned int startIndex, unsigned int length)
|
||||
{
|
||||
unsigned int stride = GetStride();
|
||||
return MapRaw(access, startIndex*stride, length*stride);
|
||||
}
|
||||
|
||||
void* NzIndexBuffer::Map(nzBufferAccess access, unsigned int startIndex, unsigned int length) const
|
||||
{
|
||||
unsigned int stride = GetStride();
|
||||
return MapRaw(access, startIndex*stride, length*stride);
|
||||
}
|
||||
|
||||
void* NzIndexBuffer::MapRaw(nzBufferAccess access, unsigned int offset, unsigned int size)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_buffer)
|
||||
{
|
||||
NazaraError("No buffer");
|
||||
return nullptr;
|
||||
ErrorFlags(ErrorFlag_ThrowException, true);
|
||||
Reset(largeIndices, buffer);
|
||||
}
|
||||
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
IndexBuffer::IndexBuffer(bool largeIndices, Buffer* buffer, unsigned int startOffset, unsigned int endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Map(access, offset, size);
|
||||
}
|
||||
|
||||
void* NzIndexBuffer::MapRaw(nzBufferAccess access, unsigned int offset, unsigned int size) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Map(access, offset, size);
|
||||
}
|
||||
|
||||
void NzIndexBuffer::Optimize()
|
||||
{
|
||||
NzIndexMapper mapper(this);
|
||||
|
||||
NzOptimizeIndices(mapper.begin(), m_indexCount);
|
||||
}
|
||||
|
||||
void NzIndexBuffer::Reset()
|
||||
{
|
||||
m_buffer.Reset();
|
||||
}
|
||||
|
||||
void NzIndexBuffer::Reset(bool largeIndices, NzBuffer* buffer)
|
||||
{
|
||||
Reset(largeIndices, buffer, 0, buffer->GetSize()-1);
|
||||
}
|
||||
|
||||
void NzIndexBuffer::Reset(bool largeIndices, NzBuffer* buffer, unsigned int startOffset, unsigned int endOffset)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!buffer || !buffer->IsValid())
|
||||
{
|
||||
NazaraError("Buffer is invalid");
|
||||
return;
|
||||
ErrorFlags(ErrorFlag_ThrowException, true);
|
||||
Reset(largeIndices, buffer, startOffset, endOffset);
|
||||
}
|
||||
|
||||
if (startOffset > endOffset)
|
||||
IndexBuffer::IndexBuffer(bool largeIndices, unsigned int length, UInt32 storage, BufferUsage usage)
|
||||
{
|
||||
NazaraError("Start offset cannot be over end offset");
|
||||
return;
|
||||
ErrorFlags(ErrorFlag_ThrowException, true);
|
||||
Reset(largeIndices, length, storage, usage);
|
||||
}
|
||||
|
||||
unsigned int bufferSize = buffer->GetSize();
|
||||
if (startOffset >= bufferSize)
|
||||
IndexBuffer::IndexBuffer(const IndexBuffer& indexBuffer) :
|
||||
RefCounted(),
|
||||
m_buffer(indexBuffer.m_buffer),
|
||||
m_largeIndices(indexBuffer.m_largeIndices),
|
||||
m_endOffset(indexBuffer.m_endOffset),
|
||||
m_indexCount(indexBuffer.m_indexCount),
|
||||
m_startOffset(indexBuffer.m_startOffset)
|
||||
{
|
||||
NazaraError("Start offset is over buffer size");
|
||||
return;
|
||||
}
|
||||
|
||||
if (endOffset >= bufferSize)
|
||||
IndexBuffer::~IndexBuffer()
|
||||
{
|
||||
NazaraError("End offset is over buffer size");
|
||||
return;
|
||||
OnIndexBufferRelease(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int stride = (largeIndices) ? sizeof(nzUInt32) : sizeof(nzUInt16);
|
||||
unsigned int IndexBuffer::ComputeCacheMissCount() const
|
||||
{
|
||||
IndexMapper mapper(this);
|
||||
|
||||
m_buffer = buffer;
|
||||
m_endOffset = endOffset;
|
||||
m_indexCount = (endOffset - startOffset) / stride;
|
||||
m_largeIndices = largeIndices;
|
||||
m_startOffset = startOffset;
|
||||
}
|
||||
|
||||
void NzIndexBuffer::Reset(bool largeIndices, unsigned int length, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
unsigned int stride = (largeIndices) ? sizeof(nzUInt32) : sizeof(nzUInt16);
|
||||
|
||||
m_endOffset = length * stride;
|
||||
m_indexCount = length;
|
||||
m_largeIndices = largeIndices;
|
||||
m_startOffset = 0;
|
||||
|
||||
m_buffer = NzBuffer::New(nzBufferType_Index, m_endOffset, storage, usage);
|
||||
}
|
||||
|
||||
void NzIndexBuffer::Reset(const NzIndexBuffer& indexBuffer)
|
||||
{
|
||||
m_buffer = indexBuffer.m_buffer;
|
||||
m_endOffset = indexBuffer.m_endOffset;
|
||||
m_indexCount = indexBuffer.m_indexCount;
|
||||
m_largeIndices = indexBuffer.m_largeIndices;
|
||||
m_startOffset = indexBuffer.m_startOffset;
|
||||
}
|
||||
|
||||
bool NzIndexBuffer::SetStorage(nzUInt32 storage)
|
||||
{
|
||||
return m_buffer->SetStorage(storage);
|
||||
}
|
||||
|
||||
void NzIndexBuffer::Unmap() const
|
||||
{
|
||||
m_buffer->Unmap();
|
||||
}
|
||||
|
||||
NzIndexBuffer& NzIndexBuffer::operator=(const NzIndexBuffer& indexBuffer)
|
||||
{
|
||||
Reset(indexBuffer);
|
||||
|
||||
return *this;
|
||||
return Nz::ComputeCacheMissCount(mapper.begin(), m_indexCount);
|
||||
}
|
||||
|
||||
bool IndexBuffer::Fill(const void* data, unsigned int startIndex, unsigned int length, bool forceDiscard)
|
||||
{
|
||||
unsigned int stride = GetStride();
|
||||
return FillRaw(data, startIndex*stride, length*stride, forceDiscard);
|
||||
}
|
||||
|
||||
bool IndexBuffer::FillRaw(const void* data, unsigned int offset, unsigned int size, bool forceDiscard)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Fill(data, m_startOffset+offset, size, forceDiscard);
|
||||
}
|
||||
|
||||
Buffer* IndexBuffer::GetBuffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
unsigned int IndexBuffer::GetEndOffset() const
|
||||
{
|
||||
return m_endOffset;
|
||||
}
|
||||
|
||||
unsigned int IndexBuffer::GetIndexCount() const
|
||||
{
|
||||
return m_indexCount;
|
||||
}
|
||||
|
||||
unsigned int IndexBuffer::GetStride() const
|
||||
{
|
||||
return (m_largeIndices) ? sizeof(UInt32) : sizeof(UInt16);
|
||||
}
|
||||
|
||||
unsigned int IndexBuffer::GetStartOffset() const
|
||||
{
|
||||
return m_startOffset;
|
||||
}
|
||||
|
||||
bool IndexBuffer::HasLargeIndices() const
|
||||
{
|
||||
return m_largeIndices;
|
||||
}
|
||||
|
||||
bool IndexBuffer::IsHardware() const
|
||||
{
|
||||
return m_buffer->IsHardware();
|
||||
}
|
||||
|
||||
bool IndexBuffer::IsValid() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
void* IndexBuffer::Map(BufferAccess access, unsigned int startIndex, unsigned int length)
|
||||
{
|
||||
unsigned int stride = GetStride();
|
||||
return MapRaw(access, startIndex*stride, length*stride);
|
||||
}
|
||||
|
||||
void* IndexBuffer::Map(BufferAccess access, unsigned int startIndex, unsigned int length) const
|
||||
{
|
||||
unsigned int stride = GetStride();
|
||||
return MapRaw(access, startIndex*stride, length*stride);
|
||||
}
|
||||
|
||||
void* IndexBuffer::MapRaw(BufferAccess access, unsigned int offset, unsigned int size)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_buffer)
|
||||
{
|
||||
NazaraError("No buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Map(access, offset, size);
|
||||
}
|
||||
|
||||
void* IndexBuffer::MapRaw(BufferAccess access, unsigned int offset, unsigned int size) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Map(access, offset, size);
|
||||
}
|
||||
|
||||
void IndexBuffer::Optimize()
|
||||
{
|
||||
IndexMapper mapper(this);
|
||||
|
||||
OptimizeIndices(mapper.begin(), m_indexCount);
|
||||
}
|
||||
|
||||
void IndexBuffer::Reset()
|
||||
{
|
||||
m_buffer.Reset();
|
||||
}
|
||||
|
||||
void IndexBuffer::Reset(bool largeIndices, Buffer* buffer)
|
||||
{
|
||||
Reset(largeIndices, buffer, 0, buffer->GetSize()-1);
|
||||
}
|
||||
|
||||
void IndexBuffer::Reset(bool largeIndices, Buffer* buffer, unsigned int startOffset, unsigned int endOffset)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!buffer || !buffer->IsValid())
|
||||
{
|
||||
NazaraError("Buffer is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (startOffset > endOffset)
|
||||
{
|
||||
NazaraError("Start offset cannot be over end offset");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int bufferSize = buffer->GetSize();
|
||||
if (startOffset >= bufferSize)
|
||||
{
|
||||
NazaraError("Start offset is over buffer size");
|
||||
return;
|
||||
}
|
||||
|
||||
if (endOffset >= bufferSize)
|
||||
{
|
||||
NazaraError("End offset is over buffer size");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int stride = (largeIndices) ? sizeof(UInt32) : sizeof(UInt16);
|
||||
|
||||
m_buffer = buffer;
|
||||
m_endOffset = endOffset;
|
||||
m_indexCount = (endOffset - startOffset) / stride;
|
||||
m_largeIndices = largeIndices;
|
||||
m_startOffset = startOffset;
|
||||
}
|
||||
|
||||
void IndexBuffer::Reset(bool largeIndices, unsigned int length, UInt32 storage, BufferUsage usage)
|
||||
{
|
||||
unsigned int stride = (largeIndices) ? sizeof(UInt32) : sizeof(UInt16);
|
||||
|
||||
m_endOffset = length * stride;
|
||||
m_indexCount = length;
|
||||
m_largeIndices = largeIndices;
|
||||
m_startOffset = 0;
|
||||
|
||||
m_buffer = Buffer::New(BufferType_Index, m_endOffset, storage, usage);
|
||||
}
|
||||
|
||||
void IndexBuffer::Reset(const IndexBuffer& indexBuffer)
|
||||
{
|
||||
m_buffer = indexBuffer.m_buffer;
|
||||
m_endOffset = indexBuffer.m_endOffset;
|
||||
m_indexCount = indexBuffer.m_indexCount;
|
||||
m_largeIndices = indexBuffer.m_largeIndices;
|
||||
m_startOffset = indexBuffer.m_startOffset;
|
||||
}
|
||||
|
||||
bool IndexBuffer::SetStorage(UInt32 storage)
|
||||
{
|
||||
return m_buffer->SetStorage(storage);
|
||||
}
|
||||
|
||||
void IndexBuffer::Unmap() const
|
||||
{
|
||||
m_buffer->Unmap();
|
||||
}
|
||||
|
||||
IndexBuffer& IndexBuffer::operator=(const IndexBuffer& indexBuffer)
|
||||
{
|
||||
Reset(indexBuffer);
|
||||
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,143 +8,146 @@
|
||||
#include <Nazara/Utility/SubMesh.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
nzUInt32 Getter16(const void* buffer, unsigned int i)
|
||||
namespace
|
||||
{
|
||||
const nzUInt16* ptr = reinterpret_cast<const nzUInt16*>(buffer);
|
||||
return ptr[i];
|
||||
UInt32 Getter16(const void* buffer, unsigned int i)
|
||||
{
|
||||
const UInt16* ptr = reinterpret_cast<const UInt16*>(buffer);
|
||||
return ptr[i];
|
||||
}
|
||||
|
||||
UInt32 Getter32(const void* buffer, unsigned int i)
|
||||
{
|
||||
const UInt32* ptr = reinterpret_cast<const UInt32*>(buffer);
|
||||
return ptr[i];
|
||||
}
|
||||
|
||||
void Setter16(void* buffer, unsigned int i, UInt32 value)
|
||||
{
|
||||
UInt16* ptr = reinterpret_cast<UInt16*>(buffer);
|
||||
ptr[i] = static_cast<UInt16>(value);
|
||||
}
|
||||
|
||||
void Setter32(void* buffer, unsigned int i, UInt32 value)
|
||||
{
|
||||
UInt32* ptr = reinterpret_cast<UInt32*>(buffer);
|
||||
ptr[i] = value;
|
||||
}
|
||||
|
||||
void SetterError(void*, unsigned int, UInt32)
|
||||
{
|
||||
NazaraError("Index buffer opened with read-only access");
|
||||
}
|
||||
}
|
||||
|
||||
nzUInt32 Getter32(const void* buffer, unsigned int i)
|
||||
IndexMapper::IndexMapper(IndexBuffer* indexBuffer, BufferAccess access) :
|
||||
m_indexCount(indexBuffer->GetIndexCount())
|
||||
{
|
||||
const nzUInt32* ptr = reinterpret_cast<const nzUInt32*>(buffer);
|
||||
return ptr[i];
|
||||
}
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!indexBuffer)
|
||||
{
|
||||
NazaraError("Index buffer must be valid");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Setter16(void* buffer, unsigned int i, nzUInt32 value)
|
||||
{
|
||||
nzUInt16* ptr = reinterpret_cast<nzUInt16*>(buffer);
|
||||
ptr[i] = static_cast<nzUInt16>(value);
|
||||
}
|
||||
if (!m_mapper.Map(indexBuffer, access))
|
||||
NazaraError("Failed to map buffer"); ///TODO: Unexcepted
|
||||
|
||||
void Setter32(void* buffer, unsigned int i, nzUInt32 value)
|
||||
{
|
||||
nzUInt32* ptr = reinterpret_cast<nzUInt32*>(buffer);
|
||||
ptr[i] = value;
|
||||
}
|
||||
|
||||
void SetterError(void*, unsigned int, nzUInt32)
|
||||
{
|
||||
NazaraError("Index buffer opened with read-only access");
|
||||
}
|
||||
}
|
||||
|
||||
NzIndexMapper::NzIndexMapper(NzIndexBuffer* indexBuffer, nzBufferAccess access) :
|
||||
m_indexCount(indexBuffer->GetIndexCount())
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!indexBuffer)
|
||||
{
|
||||
NazaraError("Index buffer must be valid");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_mapper.Map(indexBuffer, access))
|
||||
NazaraError("Failed to map buffer"); ///TODO: Unexcepted
|
||||
|
||||
if (indexBuffer->HasLargeIndices())
|
||||
{
|
||||
m_getter = Getter32;
|
||||
if (access != nzBufferAccess_ReadOnly)
|
||||
m_setter = Setter32;
|
||||
if (indexBuffer->HasLargeIndices())
|
||||
{
|
||||
m_getter = Getter32;
|
||||
if (access != BufferAccess_ReadOnly)
|
||||
m_setter = Setter32;
|
||||
else
|
||||
m_setter = SetterError;
|
||||
}
|
||||
else
|
||||
m_setter = SetterError;
|
||||
{
|
||||
m_getter = Getter16;
|
||||
if (access != BufferAccess_ReadOnly)
|
||||
m_setter = Setter16;
|
||||
else
|
||||
m_setter = SetterError;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
IndexMapper::IndexMapper(const IndexBuffer* indexBuffer, BufferAccess access) :
|
||||
m_setter(SetterError),
|
||||
m_indexCount(indexBuffer->GetIndexCount())
|
||||
{
|
||||
m_getter = Getter16;
|
||||
if (access != nzBufferAccess_ReadOnly)
|
||||
m_setter = Setter16;
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!indexBuffer)
|
||||
{
|
||||
NazaraError("Index buffer must be valid");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_mapper.Map(indexBuffer, access))
|
||||
NazaraError("Failed to map buffer"); ///TODO: Unexcepted
|
||||
|
||||
if (indexBuffer->HasLargeIndices())
|
||||
m_getter = Getter32;
|
||||
else
|
||||
m_setter = SetterError;
|
||||
m_getter = Getter16;
|
||||
}
|
||||
}
|
||||
|
||||
NzIndexMapper::NzIndexMapper(const NzIndexBuffer* indexBuffer, nzBufferAccess access) :
|
||||
m_setter(SetterError),
|
||||
m_indexCount(indexBuffer->GetIndexCount())
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!indexBuffer)
|
||||
IndexMapper::IndexMapper(const SubMesh* subMesh) :
|
||||
IndexMapper(subMesh->GetIndexBuffer())
|
||||
{
|
||||
NazaraError("Index buffer must be valid");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_mapper.Map(indexBuffer, access))
|
||||
NazaraError("Failed to map buffer"); ///TODO: Unexcepted
|
||||
|
||||
if (indexBuffer->HasLargeIndices())
|
||||
m_getter = Getter32;
|
||||
else
|
||||
m_getter = Getter16;
|
||||
}
|
||||
|
||||
NzIndexMapper::NzIndexMapper(const NzSubMesh* subMesh) :
|
||||
NzIndexMapper(subMesh->GetIndexBuffer())
|
||||
{
|
||||
}
|
||||
|
||||
nzUInt32 NzIndexMapper::Get(unsigned int i) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (i >= m_indexCount)
|
||||
UInt32 IndexMapper::Get(unsigned int i) const
|
||||
{
|
||||
NazaraError("Index out of range (" + NzString::Number(i) + " >= " + NzString::Number(m_indexCount) + ')');
|
||||
return 0;
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (i >= m_indexCount)
|
||||
{
|
||||
NazaraError("Index out of range (" + String::Number(i) + " >= " + String::Number(m_indexCount) + ')');
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_getter(m_mapper.GetPointer(), i);
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_getter(m_mapper.GetPointer(), i);
|
||||
}
|
||||
|
||||
const NzIndexBuffer* NzIndexMapper::GetBuffer() const
|
||||
{
|
||||
return m_mapper.GetBuffer();
|
||||
}
|
||||
|
||||
unsigned int NzIndexMapper::GetIndexCount() const
|
||||
{
|
||||
return m_indexCount;
|
||||
}
|
||||
|
||||
void NzIndexMapper::Set(unsigned int i, nzUInt32 value)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (i >= m_indexCount)
|
||||
const IndexBuffer* IndexMapper::GetBuffer() const
|
||||
{
|
||||
NazaraError("Index out of range (" + NzString::Number(i) + " >= " + NzString::Number(m_indexCount) + ')');
|
||||
return;
|
||||
return m_mapper.GetBuffer();
|
||||
}
|
||||
#endif
|
||||
|
||||
m_setter(m_mapper.GetPointer(), i, value);
|
||||
}
|
||||
unsigned int IndexMapper::GetIndexCount() const
|
||||
{
|
||||
return m_indexCount;
|
||||
}
|
||||
|
||||
void NzIndexMapper::Unmap()
|
||||
{
|
||||
m_mapper.Unmap();
|
||||
}
|
||||
void IndexMapper::Set(unsigned int i, UInt32 value)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (i >= m_indexCount)
|
||||
{
|
||||
NazaraError("Index out of range (" + String::Number(i) + " >= " + String::Number(m_indexCount) + ')');
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NzIndexIterator NzIndexMapper::begin()
|
||||
{
|
||||
return NzIndexIterator(this, 0);
|
||||
}
|
||||
m_setter(m_mapper.GetPointer(), i, value);
|
||||
}
|
||||
|
||||
NzIndexIterator NzIndexMapper::end()
|
||||
{
|
||||
return NzIndexIterator(this, m_indexCount); // Post-end
|
||||
void IndexMapper::Unmap()
|
||||
{
|
||||
m_mapper.Unmap();
|
||||
}
|
||||
|
||||
IndexIterator IndexMapper::begin()
|
||||
{
|
||||
return IndexIterator(this, 0);
|
||||
}
|
||||
|
||||
IndexIterator IndexMapper::end()
|
||||
{
|
||||
return IndexIterator(this, m_indexCount); // Post-end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,81 +6,84 @@
|
||||
#include <Nazara/Utility/Skeleton.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzJoint::NzJoint(NzSkeleton* skeleton) :
|
||||
m_skeleton(skeleton),
|
||||
m_skinningMatrixUpdated(false)
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
NzJoint::NzJoint(const NzJoint& joint) :
|
||||
NzNode(joint),
|
||||
m_inverseBindMatrix(joint.m_inverseBindMatrix),
|
||||
m_name(joint.m_name),
|
||||
m_skeleton(joint.m_skeleton),
|
||||
m_skinningMatrixUpdated(false)
|
||||
{
|
||||
}
|
||||
|
||||
void NzJoint::EnsureSkinningMatrixUpdate() const
|
||||
{
|
||||
if (!m_skinningMatrixUpdated)
|
||||
UpdateSkinningMatrix();
|
||||
}
|
||||
|
||||
const NzMatrix4f& NzJoint::GetInverseBindMatrix() const
|
||||
{
|
||||
return m_inverseBindMatrix;
|
||||
}
|
||||
|
||||
NzString NzJoint::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
NzSkeleton* NzJoint::GetSkeleton()
|
||||
{
|
||||
return m_skeleton;
|
||||
}
|
||||
|
||||
const NzSkeleton* NzJoint::GetSkeleton() const
|
||||
{
|
||||
return m_skeleton;
|
||||
}
|
||||
|
||||
const NzMatrix4f& NzJoint::GetSkinningMatrix() const
|
||||
{
|
||||
if (!m_skinningMatrixUpdated)
|
||||
UpdateSkinningMatrix();
|
||||
|
||||
return m_skinningMatrix;
|
||||
}
|
||||
|
||||
void NzJoint::SetInverseBindMatrix(const NzMatrix4f& matrix)
|
||||
{
|
||||
m_inverseBindMatrix = matrix;
|
||||
m_skinningMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzJoint::SetName(const NzString& name)
|
||||
{
|
||||
m_name = name;
|
||||
|
||||
m_skeleton->InvalidateJointMap();
|
||||
}
|
||||
|
||||
void NzJoint::InvalidateNode()
|
||||
{
|
||||
NzNode::InvalidateNode();
|
||||
|
||||
m_skinningMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzJoint::UpdateSkinningMatrix() const
|
||||
{
|
||||
if (!m_transformMatrixUpdated)
|
||||
UpdateTransformMatrix();
|
||||
|
||||
m_skinningMatrix.Set(m_inverseBindMatrix);
|
||||
m_skinningMatrix.ConcatenateAffine(m_transformMatrix);
|
||||
m_skinningMatrixUpdated = true;
|
||||
Joint::Joint(Skeleton* skeleton) :
|
||||
m_skeleton(skeleton),
|
||||
m_skinningMatrixUpdated(false)
|
||||
{
|
||||
}
|
||||
|
||||
Joint::Joint(const Joint& joint) :
|
||||
Node(joint),
|
||||
m_inverseBindMatrix(joint.m_inverseBindMatrix),
|
||||
m_name(joint.m_name),
|
||||
m_skeleton(joint.m_skeleton),
|
||||
m_skinningMatrixUpdated(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Joint::EnsureSkinningMatrixUpdate() const
|
||||
{
|
||||
if (!m_skinningMatrixUpdated)
|
||||
UpdateSkinningMatrix();
|
||||
}
|
||||
|
||||
const Matrix4f& Joint::GetInverseBindMatrix() const
|
||||
{
|
||||
return m_inverseBindMatrix;
|
||||
}
|
||||
|
||||
String Joint::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
Skeleton* Joint::GetSkeleton()
|
||||
{
|
||||
return m_skeleton;
|
||||
}
|
||||
|
||||
const Skeleton* Joint::GetSkeleton() const
|
||||
{
|
||||
return m_skeleton;
|
||||
}
|
||||
|
||||
const Matrix4f& Joint::GetSkinningMatrix() const
|
||||
{
|
||||
if (!m_skinningMatrixUpdated)
|
||||
UpdateSkinningMatrix();
|
||||
|
||||
return m_skinningMatrix;
|
||||
}
|
||||
|
||||
void Joint::SetInverseBindMatrix(const Matrix4f& matrix)
|
||||
{
|
||||
m_inverseBindMatrix = matrix;
|
||||
m_skinningMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void Joint::SetName(const String& name)
|
||||
{
|
||||
m_name = name;
|
||||
|
||||
m_skeleton->InvalidateJointMap();
|
||||
}
|
||||
|
||||
void Joint::InvalidateNode()
|
||||
{
|
||||
Node::InvalidateNode();
|
||||
|
||||
m_skinningMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void Joint::UpdateSkinningMatrix() const
|
||||
{
|
||||
if (!m_transformMatrixUpdated)
|
||||
UpdateTransformMatrix();
|
||||
|
||||
m_skinningMatrix.Set(m_inverseBindMatrix);
|
||||
m_skinningMatrix.ConcatenateAffine(m_transformMatrix);
|
||||
m_skinningMatrixUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzString NzKeyboard::GetKeyName(Key key)
|
||||
namespace Nz
|
||||
{
|
||||
return NzEventImpl::GetKeyName(key);
|
||||
}
|
||||
String Keyboard::GetKeyName(Key key)
|
||||
{
|
||||
return EventImpl::GetKeyName(key);
|
||||
}
|
||||
|
||||
bool NzKeyboard::IsKeyPressed(Key key)
|
||||
{
|
||||
return NzEventImpl::IsKeyPressed(key);
|
||||
bool Keyboard::IsKeyPressed(Key key)
|
||||
{
|
||||
return EventImpl::IsKeyPressed(key);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,43 +15,46 @@
|
||||
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzVector2i NzMouse::GetPosition()
|
||||
namespace Nz
|
||||
{
|
||||
return NzEventImpl::GetMousePosition();
|
||||
}
|
||||
|
||||
NzVector2i NzMouse::GetPosition(const NzWindow& relativeTo)
|
||||
{
|
||||
return NzEventImpl::GetMousePosition(relativeTo);
|
||||
}
|
||||
|
||||
bool NzMouse::IsButtonPressed(Button button)
|
||||
{
|
||||
return NzEventImpl::IsMouseButtonPressed(button);
|
||||
}
|
||||
|
||||
void NzMouse::SetPosition(const NzVector2i& position)
|
||||
{
|
||||
NzEventImpl::SetMousePosition(position.x, position.y);
|
||||
}
|
||||
|
||||
void NzMouse::SetPosition(const NzVector2i& position, const NzWindow& relativeTo, bool ignoreEvent)
|
||||
{
|
||||
if (ignoreEvent && position.x > 0 && position.y > 0)
|
||||
relativeTo.IgnoreNextMouseEvent(position.x, position.y);
|
||||
|
||||
NzEventImpl::SetMousePosition(position.x, position.y, relativeTo);
|
||||
}
|
||||
|
||||
void NzMouse::SetPosition(int x, int y)
|
||||
{
|
||||
NzEventImpl::SetMousePosition(x, y);
|
||||
}
|
||||
|
||||
void NzMouse::SetPosition(int x, int y, const NzWindow& relativeTo, bool ignoreEvent)
|
||||
{
|
||||
if (ignoreEvent && x > 0 && y > 0)
|
||||
relativeTo.IgnoreNextMouseEvent(x, y);
|
||||
|
||||
NzEventImpl::SetMousePosition(x, y, relativeTo);
|
||||
Vector2i Mouse::GetPosition()
|
||||
{
|
||||
return EventImpl::GetMousePosition();
|
||||
}
|
||||
|
||||
Vector2i Mouse::GetPosition(const Window& relativeTo)
|
||||
{
|
||||
return EventImpl::GetMousePosition(relativeTo);
|
||||
}
|
||||
|
||||
bool Mouse::IsButtonPressed(Button button)
|
||||
{
|
||||
return EventImpl::IsMouseButtonPressed(button);
|
||||
}
|
||||
|
||||
void Mouse::SetPosition(const Vector2i& position)
|
||||
{
|
||||
EventImpl::SetMousePosition(position.x, position.y);
|
||||
}
|
||||
|
||||
void Mouse::SetPosition(const Vector2i& position, const Window& relativeTo, bool ignoreEvent)
|
||||
{
|
||||
if (ignoreEvent && position.x > 0 && position.y > 0)
|
||||
relativeTo.IgnoreNextMouseEvent(position.x, position.y);
|
||||
|
||||
EventImpl::SetMousePosition(position.x, position.y, relativeTo);
|
||||
}
|
||||
|
||||
void Mouse::SetPosition(int x, int y)
|
||||
{
|
||||
EventImpl::SetMousePosition(x, y);
|
||||
}
|
||||
|
||||
void Mouse::SetPosition(int x, int y, const Window& relativeTo, bool ignoreEvent)
|
||||
{
|
||||
if (ignoreEvent && x > 0 && y > 0)
|
||||
relativeTo.IgnoreNextMouseEvent(x, y);
|
||||
|
||||
EventImpl::SetMousePosition(x, y, relativeTo);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,373 +6,376 @@
|
||||
#include <memory>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzSimpleTextDrawer::NzSimpleTextDrawer() :
|
||||
m_color(NzColor::White),
|
||||
m_style(nzTextStyle_Regular),
|
||||
m_glyphUpdated(false)
|
||||
namespace Nz
|
||||
{
|
||||
SetFont(NzFont::GetDefault());
|
||||
}
|
||||
|
||||
NzSimpleTextDrawer::NzSimpleTextDrawer(const NzSimpleTextDrawer& drawer) :
|
||||
m_color(drawer.m_color),
|
||||
m_text(drawer.m_text),
|
||||
m_style(drawer.m_style),
|
||||
m_glyphUpdated(false),
|
||||
m_characterSize(drawer.m_characterSize)
|
||||
{
|
||||
SetFont(drawer.m_font);
|
||||
}
|
||||
|
||||
NzSimpleTextDrawer::NzSimpleTextDrawer(NzSimpleTextDrawer&& drawer)
|
||||
{
|
||||
operator=(std::move(drawer));
|
||||
}
|
||||
|
||||
|
||||
NzSimpleTextDrawer::~NzSimpleTextDrawer() = default;
|
||||
|
||||
const NzRectui& NzSimpleTextDrawer::GetBounds() const
|
||||
{
|
||||
if (!m_glyphUpdated)
|
||||
UpdateGlyphs();
|
||||
|
||||
return m_bounds;
|
||||
}
|
||||
|
||||
unsigned int NzSimpleTextDrawer::GetCharacterSize() const
|
||||
{
|
||||
return m_characterSize;
|
||||
}
|
||||
|
||||
const NzColor& NzSimpleTextDrawer::GetColor() const
|
||||
{
|
||||
return m_color;
|
||||
}
|
||||
|
||||
NzFont* NzSimpleTextDrawer::GetFont() const
|
||||
{
|
||||
return m_font;
|
||||
}
|
||||
|
||||
NzFont* NzSimpleTextDrawer::GetFont(unsigned int index) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (index > 0)
|
||||
SimpleTextDrawer::SimpleTextDrawer() :
|
||||
m_color(Color::White),
|
||||
m_style(TextStyle_Regular),
|
||||
m_glyphUpdated(false)
|
||||
{
|
||||
NazaraError("Font index out of range (" + NzString::Number(index) + " >= 1)");
|
||||
return nullptr;
|
||||
SetFont(Font::GetDefault());
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_font;
|
||||
}
|
||||
|
||||
unsigned int NzSimpleTextDrawer::GetFontCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
const NzAbstractTextDrawer::Glyph& NzSimpleTextDrawer::GetGlyph(unsigned int index) const
|
||||
{
|
||||
if (!m_glyphUpdated)
|
||||
UpdateGlyphs();
|
||||
|
||||
return m_glyphs[index];
|
||||
}
|
||||
|
||||
unsigned int NzSimpleTextDrawer::GetGlyphCount() const
|
||||
{
|
||||
if (!m_glyphUpdated)
|
||||
UpdateGlyphs();
|
||||
|
||||
return m_glyphs.size();
|
||||
}
|
||||
|
||||
nzUInt32 NzSimpleTextDrawer::GetStyle() const
|
||||
{
|
||||
return m_style;
|
||||
}
|
||||
|
||||
const NzString& NzSimpleTextDrawer::GetText() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::SetCharacterSize(unsigned int characterSize)
|
||||
{
|
||||
m_characterSize = characterSize;
|
||||
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::SetColor(const NzColor& color)
|
||||
{
|
||||
m_color = color;
|
||||
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::SetFont(NzFont* font)
|
||||
{
|
||||
if (m_font != font)
|
||||
SimpleTextDrawer::SimpleTextDrawer(const SimpleTextDrawer& drawer) :
|
||||
m_color(drawer.m_color),
|
||||
m_text(drawer.m_text),
|
||||
m_style(drawer.m_style),
|
||||
m_glyphUpdated(false),
|
||||
m_characterSize(drawer.m_characterSize)
|
||||
{
|
||||
m_font = font;
|
||||
SetFont(drawer.m_font);
|
||||
}
|
||||
|
||||
if (m_font)
|
||||
ConnectFontSlots();
|
||||
else
|
||||
DisconnectFontSlots();
|
||||
SimpleTextDrawer::SimpleTextDrawer(SimpleTextDrawer&& drawer)
|
||||
{
|
||||
operator=(std::move(drawer));
|
||||
}
|
||||
|
||||
|
||||
SimpleTextDrawer::~SimpleTextDrawer() = default;
|
||||
|
||||
const Rectui& SimpleTextDrawer::GetBounds() const
|
||||
{
|
||||
if (!m_glyphUpdated)
|
||||
UpdateGlyphs();
|
||||
|
||||
return m_bounds;
|
||||
}
|
||||
|
||||
unsigned int SimpleTextDrawer::GetCharacterSize() const
|
||||
{
|
||||
return m_characterSize;
|
||||
}
|
||||
|
||||
const Color& SimpleTextDrawer::GetColor() const
|
||||
{
|
||||
return m_color;
|
||||
}
|
||||
|
||||
Font* SimpleTextDrawer::GetFont() const
|
||||
{
|
||||
return m_font;
|
||||
}
|
||||
|
||||
Font* SimpleTextDrawer::GetFont(unsigned int index) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (index > 0)
|
||||
{
|
||||
NazaraError("Font index out of range (" + String::Number(index) + " >= 1)");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_font;
|
||||
}
|
||||
|
||||
unsigned int SimpleTextDrawer::GetFontCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
const AbstractTextDrawer::Glyph& SimpleTextDrawer::GetGlyph(unsigned int index) const
|
||||
{
|
||||
if (!m_glyphUpdated)
|
||||
UpdateGlyphs();
|
||||
|
||||
return m_glyphs[index];
|
||||
}
|
||||
|
||||
unsigned int SimpleTextDrawer::GetGlyphCount() const
|
||||
{
|
||||
if (!m_glyphUpdated)
|
||||
UpdateGlyphs();
|
||||
|
||||
return m_glyphs.size();
|
||||
}
|
||||
|
||||
UInt32 SimpleTextDrawer::GetStyle() const
|
||||
{
|
||||
return m_style;
|
||||
}
|
||||
|
||||
const String& SimpleTextDrawer::GetText() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
void SimpleTextDrawer::SetCharacterSize(unsigned int characterSize)
|
||||
{
|
||||
m_characterSize = characterSize;
|
||||
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::SetStyle(nzUInt32 style)
|
||||
{
|
||||
m_style = style;
|
||||
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::SetText(const NzString& str)
|
||||
{
|
||||
m_text = str;
|
||||
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
NzSimpleTextDrawer& NzSimpleTextDrawer::operator=(NzSimpleTextDrawer&& drawer)
|
||||
{
|
||||
DisconnectFontSlots();
|
||||
|
||||
m_bounds = std::move(drawer.m_bounds);
|
||||
m_characterSize = std::move(drawer.m_characterSize);
|
||||
m_color = std::move(drawer.m_color);
|
||||
m_glyphs = std::move(drawer.m_glyphs);
|
||||
m_glyphUpdated = std::move(drawer.m_glyphUpdated);
|
||||
m_font = std::move(drawer.m_font);
|
||||
m_style = std::move(drawer.m_style);
|
||||
m_text = std::move(drawer.m_text);
|
||||
|
||||
// Update slot pointers (TODO: Improve the way of doing this)
|
||||
ConnectFontSlots();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NzSimpleTextDrawer NzSimpleTextDrawer::Draw(const NzString& str, unsigned int characterSize, nzUInt32 style, const NzColor& color)
|
||||
{
|
||||
NzSimpleTextDrawer drawer;
|
||||
drawer.SetCharacterSize(characterSize);
|
||||
drawer.SetColor(color);
|
||||
drawer.SetStyle(style);
|
||||
drawer.SetText(str);
|
||||
|
||||
return drawer;
|
||||
}
|
||||
|
||||
NzSimpleTextDrawer NzSimpleTextDrawer::Draw(NzFont* font, const NzString& str, unsigned int characterSize, nzUInt32 style, const NzColor& color)
|
||||
{
|
||||
NzSimpleTextDrawer drawer;
|
||||
drawer.SetCharacterSize(characterSize);
|
||||
drawer.SetColor(color);
|
||||
drawer.SetFont(font);
|
||||
drawer.SetStyle(style);
|
||||
drawer.SetText(str);
|
||||
|
||||
return drawer;
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::ConnectFontSlots()
|
||||
{
|
||||
m_atlasChangedSlot.Connect(m_font->OnFontAtlasChanged, this, &NzSimpleTextDrawer::OnFontInvalidated);
|
||||
m_atlasLayerChangedSlot.Connect(m_font->OnFontAtlasLayerChanged, this, &NzSimpleTextDrawer::OnFontAtlasLayerChanged);
|
||||
m_fontReleaseSlot.Connect(m_font->OnFontRelease, this, &NzSimpleTextDrawer::OnFontRelease);
|
||||
m_glyphCacheClearedSlot.Connect(m_font->OnFontGlyphCacheCleared, this, &NzSimpleTextDrawer::OnFontInvalidated);
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::DisconnectFontSlots()
|
||||
{
|
||||
m_atlasChangedSlot.Disconnect();
|
||||
m_atlasLayerChangedSlot.Disconnect();
|
||||
m_fontReleaseSlot.Disconnect();
|
||||
m_glyphCacheClearedSlot.Disconnect();
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::OnFontAtlasLayerChanged(const NzFont* font, NzAbstractImage* oldLayer, NzAbstractImage* newLayer)
|
||||
{
|
||||
NazaraUnused(font);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (m_font != font)
|
||||
void SimpleTextDrawer::SetColor(const Color& color)
|
||||
{
|
||||
NazaraInternalError("Not listening to " + NzString::Pointer(font));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
m_color = color;
|
||||
|
||||
// Update atlas layer pointer
|
||||
// Note: This can happend while updating
|
||||
for (Glyph& glyph : m_glyphs)
|
||||
{
|
||||
if (glyph.atlas == oldLayer)
|
||||
glyph.atlas = newLayer;
|
||||
}
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::OnFontInvalidated(const NzFont* font)
|
||||
{
|
||||
NazaraUnused(font);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (m_font != font)
|
||||
{
|
||||
NazaraInternalError("Not listening to " + NzString::Pointer(font));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::OnFontRelease(const NzFont* font)
|
||||
{
|
||||
NazaraUnused(font);
|
||||
NazaraUnused(font);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (m_font != font)
|
||||
{
|
||||
NazaraInternalError("Not listening to " + NzString::Pointer(font));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
SetFont(nullptr);
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::UpdateGlyphs() const
|
||||
{
|
||||
m_bounds.MakeZero();
|
||||
m_glyphs.clear();
|
||||
m_glyphUpdated = true;
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_font || !m_font->IsValid())
|
||||
{
|
||||
NazaraError("Invalid font");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_text.IsEmpty())
|
||||
return;
|
||||
|
||||
///TODO: Itération UTF-8 => UTF-32 sans allocation de buffer (Exposer utf8cpp ?)
|
||||
std::u32string characters = m_text.GetUtf32String();
|
||||
if (characters.empty())
|
||||
{
|
||||
NazaraError("Invalid character set");
|
||||
return;
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
const NzFont::SizeInfo& sizeInfo = m_font->GetSizeInfo(m_characterSize);
|
||||
|
||||
// "Curseur" de dessin
|
||||
NzVector2ui drawPos(0, m_characterSize);
|
||||
|
||||
// On calcule les bornes en flottants pour accélérer les calculs (il est coûteux de changer de type trop souvent)
|
||||
bool firstGlyph = true;
|
||||
NzRectf textBounds = NzRectf::Zero();
|
||||
nzUInt32 previousCharacter = 0;
|
||||
|
||||
m_glyphs.reserve(characters.size());
|
||||
for (char32_t character : characters)
|
||||
void SimpleTextDrawer::SetFont(Font* font)
|
||||
{
|
||||
if (previousCharacter != 0)
|
||||
drawPos.x += m_font->GetKerning(m_characterSize, previousCharacter, character);
|
||||
|
||||
previousCharacter = character;
|
||||
|
||||
bool whitespace = true;
|
||||
switch (character)
|
||||
if (m_font != font)
|
||||
{
|
||||
case ' ':
|
||||
drawPos.x += sizeInfo.spaceAdvance;
|
||||
break;
|
||||
m_font = font;
|
||||
|
||||
case '\n':
|
||||
drawPos.x = 0;
|
||||
drawPos.y += sizeInfo.lineHeight;
|
||||
break;
|
||||
if (m_font)
|
||||
ConnectFontSlots();
|
||||
else
|
||||
DisconnectFontSlots();
|
||||
|
||||
case '\t':
|
||||
drawPos.x += sizeInfo.spaceAdvance*4;
|
||||
break;
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
whitespace = false;
|
||||
break;
|
||||
void SimpleTextDrawer::SetStyle(UInt32 style)
|
||||
{
|
||||
m_style = style;
|
||||
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
void SimpleTextDrawer::SetText(const String& str)
|
||||
{
|
||||
m_text = str;
|
||||
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
SimpleTextDrawer& SimpleTextDrawer::operator=(SimpleTextDrawer&& drawer)
|
||||
{
|
||||
DisconnectFontSlots();
|
||||
|
||||
m_bounds = std::move(drawer.m_bounds);
|
||||
m_characterSize = std::move(drawer.m_characterSize);
|
||||
m_color = std::move(drawer.m_color);
|
||||
m_glyphs = std::move(drawer.m_glyphs);
|
||||
m_glyphUpdated = std::move(drawer.m_glyphUpdated);
|
||||
m_font = std::move(drawer.m_font);
|
||||
m_style = std::move(drawer.m_style);
|
||||
m_text = std::move(drawer.m_text);
|
||||
|
||||
// Update slot pointers (TODO: Improve the way of doing this)
|
||||
ConnectFontSlots();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
SimpleTextDrawer SimpleTextDrawer::Draw(const String& str, unsigned int characterSize, UInt32 style, const Color& color)
|
||||
{
|
||||
SimpleTextDrawer drawer;
|
||||
drawer.SetCharacterSize(characterSize);
|
||||
drawer.SetColor(color);
|
||||
drawer.SetStyle(style);
|
||||
drawer.SetText(str);
|
||||
|
||||
return drawer;
|
||||
}
|
||||
|
||||
SimpleTextDrawer SimpleTextDrawer::Draw(Font* font, const String& str, unsigned int characterSize, UInt32 style, const Color& color)
|
||||
{
|
||||
SimpleTextDrawer drawer;
|
||||
drawer.SetCharacterSize(characterSize);
|
||||
drawer.SetColor(color);
|
||||
drawer.SetFont(font);
|
||||
drawer.SetStyle(style);
|
||||
drawer.SetText(str);
|
||||
|
||||
return drawer;
|
||||
}
|
||||
|
||||
void SimpleTextDrawer::ConnectFontSlots()
|
||||
{
|
||||
m_atlasChangedSlot.Connect(m_font->OnFontAtlasChanged, this, &SimpleTextDrawer::OnFontInvalidated);
|
||||
m_atlasLayerChangedSlot.Connect(m_font->OnFontAtlasLayerChanged, this, &SimpleTextDrawer::OnFontAtlasLayerChanged);
|
||||
m_fontReleaseSlot.Connect(m_font->OnFontRelease, this, &SimpleTextDrawer::OnFontRelease);
|
||||
m_glyphCacheClearedSlot.Connect(m_font->OnFontGlyphCacheCleared, this, &SimpleTextDrawer::OnFontInvalidated);
|
||||
}
|
||||
|
||||
void SimpleTextDrawer::DisconnectFontSlots()
|
||||
{
|
||||
m_atlasChangedSlot.Disconnect();
|
||||
m_atlasLayerChangedSlot.Disconnect();
|
||||
m_fontReleaseSlot.Disconnect();
|
||||
m_glyphCacheClearedSlot.Disconnect();
|
||||
}
|
||||
|
||||
void SimpleTextDrawer::OnFontAtlasLayerChanged(const Font* font, AbstractImage* oldLayer, AbstractImage* newLayer)
|
||||
{
|
||||
NazaraUnused(font);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (m_font != font)
|
||||
{
|
||||
NazaraInternalError("Not listening to " + String::Pointer(font));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update atlas layer pointer
|
||||
// Note: This can happend while updating
|
||||
for (Glyph& glyph : m_glyphs)
|
||||
{
|
||||
if (glyph.atlas == oldLayer)
|
||||
glyph.atlas = newLayer;
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleTextDrawer::OnFontInvalidated(const Font* font)
|
||||
{
|
||||
NazaraUnused(font);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (m_font != font)
|
||||
{
|
||||
NazaraInternalError("Not listening to " + String::Pointer(font));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
void SimpleTextDrawer::OnFontRelease(const Font* font)
|
||||
{
|
||||
NazaraUnused(font);
|
||||
NazaraUnused(font);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (m_font != font)
|
||||
{
|
||||
NazaraInternalError("Not listening to " + String::Pointer(font));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
SetFont(nullptr);
|
||||
}
|
||||
|
||||
void SimpleTextDrawer::UpdateGlyphs() const
|
||||
{
|
||||
m_bounds.MakeZero();
|
||||
m_glyphs.clear();
|
||||
m_glyphUpdated = true;
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_font || !m_font->IsValid())
|
||||
{
|
||||
NazaraError("Invalid font");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_text.IsEmpty())
|
||||
return;
|
||||
|
||||
///TODO: Itération UTF-8 => UTF-32 sans allocation de buffer (Exposer utf8cpp ?)
|
||||
std::u32string characters = m_text.GetUtf32String();
|
||||
if (characters.empty())
|
||||
{
|
||||
NazaraError("Invalid character set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (whitespace)
|
||||
continue; // Inutile d'avoir un glyphe pour un espace blanc
|
||||
const Font::SizeInfo& sizeInfo = m_font->GetSizeInfo(m_characterSize);
|
||||
|
||||
const NzFont::Glyph& fontGlyph = m_font->GetGlyph(m_characterSize, m_style, character);
|
||||
if (!fontGlyph.valid)
|
||||
continue; // Le glyphe n'a pas été correctement chargé, que pouvons-nous faire d'autre que le passer
|
||||
// "Curseur" de dessin
|
||||
Vector2ui drawPos(0, m_characterSize);
|
||||
|
||||
Glyph glyph;
|
||||
glyph.atlas = m_font->GetAtlas()->GetLayer(fontGlyph.layerIndex);
|
||||
glyph.atlasRect = fontGlyph.atlasRect;
|
||||
glyph.color = m_color;
|
||||
glyph.flipped = fontGlyph.flipped;
|
||||
// On calcule les bornes en flottants pour accélérer les calculs (il est coûteux de changer de type trop souvent)
|
||||
bool firstGlyph = true;
|
||||
Rectf textBounds = Rectf::Zero();
|
||||
UInt32 previousCharacter = 0;
|
||||
|
||||
int advance = fontGlyph.advance;
|
||||
|
||||
NzRectf bounds(fontGlyph.aabb);
|
||||
bounds.x += drawPos.x;
|
||||
bounds.y += drawPos.y;
|
||||
|
||||
if (fontGlyph.requireFauxBold)
|
||||
m_glyphs.reserve(characters.size());
|
||||
for (char32_t character : characters)
|
||||
{
|
||||
// On va agrandir le glyphe pour simuler le gras (idée moisie, mais idée quand même)
|
||||
NzVector2f center = bounds.GetCenter();
|
||||
if (previousCharacter != 0)
|
||||
drawPos.x += m_font->GetKerning(m_characterSize, previousCharacter, character);
|
||||
|
||||
bounds.width *= 1.1f;
|
||||
bounds.height *= 1.1f;
|
||||
previousCharacter = character;
|
||||
|
||||
// On le replace à la bonne hauteur
|
||||
NzVector2f offset(bounds.GetCenter() - center);
|
||||
bounds.y -= offset.y;
|
||||
bool whitespace = true;
|
||||
switch (character)
|
||||
{
|
||||
case ' ':
|
||||
drawPos.x += sizeInfo.spaceAdvance;
|
||||
break;
|
||||
|
||||
// On ajuste l'espacement
|
||||
advance += advance/10;
|
||||
case '\n':
|
||||
drawPos.x = 0;
|
||||
drawPos.y += sizeInfo.lineHeight;
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
drawPos.x += sizeInfo.spaceAdvance*4;
|
||||
break;
|
||||
|
||||
default:
|
||||
whitespace = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (whitespace)
|
||||
continue; // Inutile d'avoir un glyphe pour un espace blanc
|
||||
|
||||
const Font::Glyph& fontGlyph = m_font->GetGlyph(m_characterSize, m_style, character);
|
||||
if (!fontGlyph.valid)
|
||||
continue; // Le glyphe n'a pas été correctement chargé, que pouvons-nous faire d'autre que le passer
|
||||
|
||||
Glyph glyph;
|
||||
glyph.atlas = m_font->GetAtlas()->GetLayer(fontGlyph.layerIndex);
|
||||
glyph.atlasRect = fontGlyph.atlasRect;
|
||||
glyph.color = m_color;
|
||||
glyph.flipped = fontGlyph.flipped;
|
||||
|
||||
int advance = fontGlyph.advance;
|
||||
|
||||
Rectf bounds(fontGlyph.aabb);
|
||||
bounds.x += drawPos.x;
|
||||
bounds.y += drawPos.y;
|
||||
|
||||
if (fontGlyph.requireFauxBold)
|
||||
{
|
||||
// On va agrandir le glyphe pour simuler le gras (idée moisie, mais idée quand même)
|
||||
Vector2f center = bounds.GetCenter();
|
||||
|
||||
bounds.width *= 1.1f;
|
||||
bounds.height *= 1.1f;
|
||||
|
||||
// On le replace à la bonne hauteur
|
||||
Vector2f offset(bounds.GetCenter() - center);
|
||||
bounds.y -= offset.y;
|
||||
|
||||
// On ajuste l'espacement
|
||||
advance += advance/10;
|
||||
}
|
||||
|
||||
// On "penche" le glyphe pour obtenir un semblant d'italique
|
||||
float italic = (fontGlyph.requireFauxItalic) ? 0.208f : 0.f;
|
||||
float italicTop = italic * bounds.y;
|
||||
float italicBottom = italic * bounds.GetMaximum().y;
|
||||
|
||||
glyph.corners[0].Set(bounds.x - italicTop, bounds.y);
|
||||
glyph.corners[1].Set(bounds.x + bounds.width - italicTop, bounds.y);
|
||||
glyph.corners[2].Set(bounds.x - italicBottom, bounds.y + bounds.height);
|
||||
glyph.corners[3].Set(bounds.x + bounds.width - italicBottom, bounds.y + bounds.height);
|
||||
|
||||
if (firstGlyph)
|
||||
{
|
||||
textBounds.Set(glyph.corners[0]);
|
||||
firstGlyph = false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
textBounds.ExtendTo(glyph.corners[i]);
|
||||
|
||||
drawPos.x += advance;
|
||||
m_glyphs.push_back(glyph);
|
||||
}
|
||||
|
||||
// On "penche" le glyphe pour obtenir un semblant d'italique
|
||||
float italic = (fontGlyph.requireFauxItalic) ? 0.208f : 0.f;
|
||||
float italicTop = italic * bounds.y;
|
||||
float italicBottom = italic * bounds.GetMaximum().y;
|
||||
|
||||
glyph.corners[0].Set(bounds.x - italicTop, bounds.y);
|
||||
glyph.corners[1].Set(bounds.x + bounds.width - italicTop, bounds.y);
|
||||
glyph.corners[2].Set(bounds.x - italicBottom, bounds.y + bounds.height);
|
||||
glyph.corners[3].Set(bounds.x + bounds.width - italicBottom, bounds.y + bounds.height);
|
||||
|
||||
if (firstGlyph)
|
||||
{
|
||||
textBounds.Set(glyph.corners[0]);
|
||||
firstGlyph = false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
textBounds.ExtendTo(glyph.corners[i]);
|
||||
|
||||
drawPos.x += advance;
|
||||
m_glyphs.push_back(glyph);
|
||||
m_bounds.Set(Rectf(std::floor(textBounds.x), std::floor(textBounds.y), std::ceil(textBounds.width), std::ceil(textBounds.height)));
|
||||
}
|
||||
|
||||
m_bounds.Set(NzRectf(std::floor(textBounds.x), std::floor(textBounds.y), std::ceil(textBounds.width), std::ceil(textBounds.height)));
|
||||
}
|
||||
|
||||
@@ -9,91 +9,94 @@
|
||||
#include <vector>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzSkeletalMesh::NzSkeletalMesh(const NzMesh* parent) :
|
||||
NzSubMesh(parent)
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
NzSkeletalMesh::~NzSkeletalMesh()
|
||||
{
|
||||
OnSkeletalMeshRelease(this);
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool NzSkeletalMesh::Create(NzVertexBuffer* vertexBuffer)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!vertexBuffer)
|
||||
SkeletalMesh::SkeletalMesh(const Mesh* parent) :
|
||||
SubMesh(parent)
|
||||
{
|
||||
NazaraError("Invalid vertex buffer");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_vertexBuffer = vertexBuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzSkeletalMesh::Destroy()
|
||||
{
|
||||
if (m_vertexBuffer)
|
||||
SkeletalMesh::~SkeletalMesh()
|
||||
{
|
||||
OnSkeletalMeshDestroy(this);
|
||||
OnSkeletalMeshRelease(this);
|
||||
|
||||
m_indexBuffer.Reset();
|
||||
m_vertexBuffer.Reset();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool SkeletalMesh::Create(VertexBuffer* vertexBuffer)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!vertexBuffer)
|
||||
{
|
||||
NazaraError("Invalid vertex buffer");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_vertexBuffer = vertexBuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkeletalMesh::Destroy()
|
||||
{
|
||||
if (m_vertexBuffer)
|
||||
{
|
||||
OnSkeletalMeshDestroy(this);
|
||||
|
||||
m_indexBuffer.Reset();
|
||||
m_vertexBuffer.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
const Boxf& SkeletalMesh::GetAABB() const
|
||||
{
|
||||
return m_aabb;
|
||||
}
|
||||
|
||||
AnimationType SkeletalMesh::GetAnimationType() const
|
||||
{
|
||||
return AnimationType_Skeletal;
|
||||
}
|
||||
|
||||
const IndexBuffer* SkeletalMesh::GetIndexBuffer() const
|
||||
{
|
||||
return m_indexBuffer;
|
||||
}
|
||||
|
||||
VertexBuffer* SkeletalMesh::GetVertexBuffer()
|
||||
{
|
||||
return m_vertexBuffer;
|
||||
}
|
||||
|
||||
const VertexBuffer* SkeletalMesh::GetVertexBuffer() const
|
||||
{
|
||||
return m_vertexBuffer;
|
||||
}
|
||||
|
||||
unsigned int SkeletalMesh::GetVertexCount() const
|
||||
{
|
||||
return m_vertexBuffer->GetVertexCount();
|
||||
}
|
||||
|
||||
bool SkeletalMesh::IsAnimated() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkeletalMesh::IsValid() const
|
||||
{
|
||||
return m_vertexBuffer != nullptr;
|
||||
}
|
||||
|
||||
void SkeletalMesh::SetAABB(const Boxf& aabb)
|
||||
{
|
||||
m_aabb = aabb;
|
||||
}
|
||||
|
||||
void SkeletalMesh::SetIndexBuffer(const IndexBuffer* indexBuffer)
|
||||
{
|
||||
m_indexBuffer = indexBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
const NzBoxf& NzSkeletalMesh::GetAABB() const
|
||||
{
|
||||
return m_aabb;
|
||||
}
|
||||
|
||||
nzAnimationType NzSkeletalMesh::GetAnimationType() const
|
||||
{
|
||||
return nzAnimationType_Skeletal;
|
||||
}
|
||||
|
||||
const NzIndexBuffer* NzSkeletalMesh::GetIndexBuffer() const
|
||||
{
|
||||
return m_indexBuffer;
|
||||
}
|
||||
|
||||
NzVertexBuffer* NzSkeletalMesh::GetVertexBuffer()
|
||||
{
|
||||
return m_vertexBuffer;
|
||||
}
|
||||
|
||||
const NzVertexBuffer* NzSkeletalMesh::GetVertexBuffer() const
|
||||
{
|
||||
return m_vertexBuffer;
|
||||
}
|
||||
|
||||
unsigned int NzSkeletalMesh::GetVertexCount() const
|
||||
{
|
||||
return m_vertexBuffer->GetVertexCount();
|
||||
}
|
||||
|
||||
bool NzSkeletalMesh::IsAnimated() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzSkeletalMesh::IsValid() const
|
||||
{
|
||||
return m_vertexBuffer != nullptr;
|
||||
}
|
||||
|
||||
void NzSkeletalMesh::SetAABB(const NzBoxf& aabb)
|
||||
{
|
||||
m_aabb = aabb;
|
||||
}
|
||||
|
||||
void NzSkeletalMesh::SetIndexBuffer(const NzIndexBuffer* indexBuffer)
|
||||
{
|
||||
m_indexBuffer = indexBuffer;
|
||||
}
|
||||
|
||||
@@ -6,439 +6,442 @@
|
||||
#include <unordered_map>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
struct NzSkeletonImpl
|
||||
namespace Nz
|
||||
{
|
||||
std::unordered_map<NzString, unsigned int> jointMap;
|
||||
std::vector<NzJoint> joints;
|
||||
NzBoxf aabb;
|
||||
bool aabbUpdated = false;
|
||||
bool jointMapUpdated = false;
|
||||
};
|
||||
|
||||
NzSkeleton::NzSkeleton(const NzSkeleton& skeleton) :
|
||||
NzRefCounted(),
|
||||
m_impl(nullptr)
|
||||
{
|
||||
operator=(skeleton);
|
||||
}
|
||||
|
||||
NzSkeleton::~NzSkeleton()
|
||||
{
|
||||
OnSkeletonRelease(this);
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool NzSkeleton::Create(unsigned int jointCount)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (jointCount == 0)
|
||||
struct SkeletonImpl
|
||||
{
|
||||
NazaraError("Joint count must be over zero");
|
||||
return false;
|
||||
std::unordered_map<String, unsigned int> jointMap;
|
||||
std::vector<Joint> joints;
|
||||
Boxf aabb;
|
||||
bool aabbUpdated = false;
|
||||
bool jointMapUpdated = false;
|
||||
};
|
||||
|
||||
Skeleton::Skeleton(const Skeleton& skeleton) :
|
||||
RefCounted(),
|
||||
m_impl(nullptr)
|
||||
{
|
||||
operator=(skeleton);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl = new NzSkeletonImpl;
|
||||
m_impl->joints.resize(jointCount, NzJoint(this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzSkeleton::Destroy()
|
||||
{
|
||||
if (m_impl)
|
||||
Skeleton::~Skeleton()
|
||||
{
|
||||
OnSkeletonDestroy(this);
|
||||
OnSkeletonRelease(this);
|
||||
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
const NzBoxf& NzSkeleton::GetAABB() const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
bool Skeleton::Create(unsigned int jointCount)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
|
||||
static NzBoxf dummy;
|
||||
return dummy;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_impl->aabbUpdated)
|
||||
{
|
||||
unsigned int jointCount = m_impl->joints.size();
|
||||
if (jointCount > 0)
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (jointCount == 0)
|
||||
{
|
||||
NzVector3f pos = m_impl->joints[0].GetPosition();
|
||||
m_impl->aabb.Set(pos.x, pos.y, pos.z, 0.f, 0.f, 0.f);
|
||||
for (unsigned int i = 1; i < jointCount; ++i)
|
||||
m_impl->aabb.ExtendTo(m_impl->joints[i].GetPosition());
|
||||
NazaraError("Joint count must be over zero");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
m_impl->aabb.MakeZero();
|
||||
#endif
|
||||
|
||||
m_impl->aabbUpdated = true;
|
||||
m_impl = new SkeletonImpl;
|
||||
m_impl->joints.resize(jointCount, Joint(this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_impl->aabb;
|
||||
}
|
||||
|
||||
NzJoint* NzSkeleton::GetJoint(const NzString& jointName)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
void Skeleton::Destroy()
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
if (m_impl)
|
||||
{
|
||||
OnSkeletonDestroy(this);
|
||||
|
||||
if (!m_impl->jointMapUpdated)
|
||||
UpdateJointMap();
|
||||
|
||||
auto it = m_impl->jointMap.find(jointName);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (it == m_impl->jointMap.end())
|
||||
{
|
||||
NazaraError("Joint not found");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
InvalidateJoints();
|
||||
|
||||
return &m_impl->joints[it->second];
|
||||
}
|
||||
|
||||
NzJoint* NzSkeleton::GetJoint(unsigned int index)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= m_impl->joints.size())
|
||||
const Boxf& Skeleton::GetAABB() const
|
||||
{
|
||||
NazaraError("Joint index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->joints.size()) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
|
||||
InvalidateJoints();
|
||||
static Boxf dummy;
|
||||
return dummy;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &m_impl->joints[index];
|
||||
}
|
||||
if (!m_impl->aabbUpdated)
|
||||
{
|
||||
unsigned int jointCount = m_impl->joints.size();
|
||||
if (jointCount > 0)
|
||||
{
|
||||
Vector3f pos = m_impl->joints[0].GetPosition();
|
||||
m_impl->aabb.Set(pos.x, pos.y, pos.z, 0.f, 0.f, 0.f);
|
||||
for (unsigned int i = 1; i < jointCount; ++i)
|
||||
m_impl->aabb.ExtendTo(m_impl->joints[i].GetPosition());
|
||||
}
|
||||
else
|
||||
m_impl->aabb.MakeZero();
|
||||
|
||||
const NzJoint* NzSkeleton::GetJoint(const NzString& jointName) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
m_impl->aabbUpdated = true;
|
||||
}
|
||||
|
||||
if (!m_impl->jointMapUpdated)
|
||||
UpdateJointMap();
|
||||
|
||||
auto it = m_impl->jointMap.find(jointName);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (it == m_impl->jointMap.end())
|
||||
{
|
||||
NazaraError("Joint not found");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &m_impl->joints[it->second];
|
||||
}
|
||||
|
||||
const NzJoint* NzSkeleton::GetJoint(unsigned int index) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
return m_impl->aabb;
|
||||
}
|
||||
|
||||
if (index >= m_impl->joints.size())
|
||||
Joint* Skeleton::GetJoint(const String& jointName)
|
||||
{
|
||||
NazaraError("Joint index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->joints.size()) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &m_impl->joints[index];
|
||||
}
|
||||
if (!m_impl->jointMapUpdated)
|
||||
UpdateJointMap();
|
||||
|
||||
NzJoint* NzSkeleton::GetJoints()
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &m_impl->joints[0];
|
||||
}
|
||||
|
||||
const NzJoint* NzSkeleton::GetJoints() const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &m_impl->joints[0];
|
||||
}
|
||||
|
||||
unsigned int NzSkeleton::GetJointCount() const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_impl->joints.size();
|
||||
}
|
||||
|
||||
int NzSkeleton::GetJointIndex(const NzString& jointName) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_impl->jointMapUpdated)
|
||||
UpdateJointMap();
|
||||
|
||||
auto it = m_impl->jointMap.find(jointName);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (it == m_impl->jointMap.end())
|
||||
{
|
||||
NazaraError("Joint not found");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void NzSkeleton::Interpolate(const NzSkeleton& skeletonA, const NzSkeleton& skeletonB, float interpolation)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skeletonA.IsValid())
|
||||
{
|
||||
NazaraError("Skeleton A is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skeletonB.IsValid())
|
||||
{
|
||||
NazaraError("Skeleton B is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (skeletonA.GetJointCount() != skeletonB.GetJointCount() || m_impl->joints.size() != skeletonA.GetJointCount())
|
||||
{
|
||||
NazaraError("Skeletons must have the same joint count");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NzJoint* jointsA = &skeletonA.m_impl->joints[0];
|
||||
NzJoint* jointsB = &skeletonB.m_impl->joints[0];
|
||||
for (unsigned int i = 0; i < m_impl->joints.size(); ++i)
|
||||
m_impl->joints[i].Interpolate(jointsA[i], jointsB[i], interpolation, nzCoordSys_Local);
|
||||
|
||||
InvalidateJoints();
|
||||
}
|
||||
|
||||
void NzSkeleton::Interpolate(const NzSkeleton& skeletonA, const NzSkeleton& skeletonB, float interpolation, unsigned int* indices, unsigned int indiceCount)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skeletonA.IsValid())
|
||||
{
|
||||
NazaraError("Skeleton A is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skeletonB.IsValid())
|
||||
{
|
||||
NazaraError("Skeleton B is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (skeletonA.GetJointCount() != skeletonB.GetJointCount() || m_impl->joints.size() != skeletonA.GetJointCount())
|
||||
{
|
||||
NazaraError("Skeletons must have the same joint count");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
const NzJoint* jointsA = &skeletonA.m_impl->joints[0];
|
||||
const NzJoint* jointsB = &skeletonB.m_impl->joints[0];
|
||||
for (unsigned int i = 0; i < indiceCount; ++i)
|
||||
{
|
||||
unsigned int index = indices[i];
|
||||
auto it = m_impl->jointMap.find(jointName);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (it == m_impl->jointMap.end())
|
||||
{
|
||||
NazaraError("Joint not found");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
InvalidateJoints();
|
||||
|
||||
return &m_impl->joints[it->second];
|
||||
}
|
||||
|
||||
Joint* Skeleton::GetJoint(unsigned int index)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (index >= m_impl->joints.size())
|
||||
{
|
||||
NazaraError("Index #" + NzString::Number(i) + " out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->joints.size()) + ')');
|
||||
NazaraError("Joint index out of range (" + String::Number(index) + " >= " + String::Number(m_impl->joints.size()) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
InvalidateJoints();
|
||||
|
||||
return &m_impl->joints[index];
|
||||
}
|
||||
|
||||
const Joint* Skeleton::GetJoint(const String& jointName) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_impl->jointMapUpdated)
|
||||
UpdateJointMap();
|
||||
|
||||
auto it = m_impl->jointMap.find(jointName);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (it == m_impl->jointMap.end())
|
||||
{
|
||||
NazaraError("Joint not found");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &m_impl->joints[it->second];
|
||||
}
|
||||
|
||||
const Joint* Skeleton::GetJoint(unsigned int index) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (index >= m_impl->joints.size())
|
||||
{
|
||||
NazaraError("Joint index out of range (" + String::Number(index) + " >= " + String::Number(m_impl->joints.size()) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &m_impl->joints[index];
|
||||
}
|
||||
|
||||
Joint* Skeleton::GetJoints()
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &m_impl->joints[0];
|
||||
}
|
||||
|
||||
const Joint* Skeleton::GetJoints() const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &m_impl->joints[0];
|
||||
}
|
||||
|
||||
unsigned int Skeleton::GetJointCount() const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_impl->joints.size();
|
||||
}
|
||||
|
||||
int Skeleton::GetJointIndex(const String& jointName) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_impl->jointMapUpdated)
|
||||
UpdateJointMap();
|
||||
|
||||
auto it = m_impl->jointMap.find(jointName);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (it == m_impl->jointMap.end())
|
||||
{
|
||||
NazaraError("Joint not found");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void Skeleton::Interpolate(const Skeleton& skeletonA, const Skeleton& skeletonB, float interpolation)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Skeleton not created");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skeletonA.IsValid())
|
||||
{
|
||||
NazaraError("Skeleton A is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skeletonB.IsValid())
|
||||
{
|
||||
NazaraError("Skeleton B is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (skeletonA.GetJointCount() != skeletonB.GetJointCount() || m_impl->joints.size() != skeletonA.GetJointCount())
|
||||
{
|
||||
NazaraError("Skeletons must have the same joint count");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->joints[index].Interpolate(jointsA[index], jointsB[index], interpolation, nzCoordSys_Local);
|
||||
Joint* jointsA = &skeletonA.m_impl->joints[0];
|
||||
Joint* jointsB = &skeletonB.m_impl->joints[0];
|
||||
for (unsigned int i = 0; i < m_impl->joints.size(); ++i)
|
||||
m_impl->joints[i].Interpolate(jointsA[i], jointsB[i], interpolation, CoordSys_Local);
|
||||
|
||||
InvalidateJoints();
|
||||
}
|
||||
|
||||
InvalidateJoints();
|
||||
}
|
||||
|
||||
bool NzSkeleton::IsValid() const
|
||||
{
|
||||
return m_impl != nullptr;
|
||||
}
|
||||
|
||||
NzSkeleton& NzSkeleton::operator=(const NzSkeleton& skeleton)
|
||||
{
|
||||
if (this == &skeleton)
|
||||
return *this;
|
||||
|
||||
Destroy();
|
||||
|
||||
if (skeleton.m_impl)
|
||||
void Skeleton::Interpolate(const Skeleton& skeletonA, const Skeleton& skeletonB, float interpolation, unsigned int* indices, unsigned int indiceCount)
|
||||
{
|
||||
m_impl = new NzSkeletonImpl;
|
||||
m_impl->jointMap = skeleton.m_impl->jointMap;
|
||||
m_impl->jointMapUpdated = skeleton.m_impl->jointMapUpdated;
|
||||
m_impl->joints = skeleton.m_impl->joints;
|
||||
|
||||
// Code crade mais son optimisation demanderait de stocker jointCount*sizeof(unsigned int) en plus
|
||||
// Ce qui, pour juste une copie qui ne se fera que rarement, ne vaut pas le coup
|
||||
// L'éternel trade-off mémoire/calculs ..
|
||||
unsigned int jointCount = skeleton.m_impl->joints.size();
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
const NzNode* parent = skeleton.m_impl->joints[i].GetParent();
|
||||
if (parent)
|
||||
NazaraError("Skeleton not created");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skeletonA.IsValid())
|
||||
{
|
||||
NazaraError("Skeleton A is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skeletonB.IsValid())
|
||||
{
|
||||
NazaraError("Skeleton B is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (skeletonA.GetJointCount() != skeletonB.GetJointCount() || m_impl->joints.size() != skeletonA.GetJointCount())
|
||||
{
|
||||
NazaraError("Skeletons must have the same joint count");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
const Joint* jointsA = &skeletonA.m_impl->joints[0];
|
||||
const Joint* jointsB = &skeletonB.m_impl->joints[0];
|
||||
for (unsigned int i = 0; i < indiceCount; ++i)
|
||||
{
|
||||
unsigned int index = indices[i];
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (index >= m_impl->joints.size())
|
||||
{
|
||||
for (unsigned int j = 0; j < i; ++j) // Le parent se trouve forcément avant nous
|
||||
NazaraError("Index #" + String::Number(i) + " out of range (" + String::Number(index) + " >= " + String::Number(m_impl->joints.size()) + ')');
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->joints[index].Interpolate(jointsA[index], jointsB[index], interpolation, CoordSys_Local);
|
||||
}
|
||||
|
||||
InvalidateJoints();
|
||||
}
|
||||
|
||||
bool Skeleton::IsValid() const
|
||||
{
|
||||
return m_impl != nullptr;
|
||||
}
|
||||
|
||||
Skeleton& Skeleton::operator=(const Skeleton& skeleton)
|
||||
{
|
||||
if (this == &skeleton)
|
||||
return *this;
|
||||
|
||||
Destroy();
|
||||
|
||||
if (skeleton.m_impl)
|
||||
{
|
||||
m_impl = new SkeletonImpl;
|
||||
m_impl->jointMap = skeleton.m_impl->jointMap;
|
||||
m_impl->jointMapUpdated = skeleton.m_impl->jointMapUpdated;
|
||||
m_impl->joints = skeleton.m_impl->joints;
|
||||
|
||||
// Code crade mais son optimisation demanderait de stocker jointCount*sizeof(unsigned int) en plus
|
||||
// Ce qui, pour juste une copie qui ne se fera que rarement, ne vaut pas le coup
|
||||
// L'éternel trade-off mémoire/calculs ..
|
||||
unsigned int jointCount = skeleton.m_impl->joints.size();
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
const Node* parent = skeleton.m_impl->joints[i].GetParent();
|
||||
if (parent)
|
||||
{
|
||||
if (parent == &skeleton.m_impl->joints[j]) // A-t-on trouvé le parent ?
|
||||
for (unsigned int j = 0; j < i; ++j) // Le parent se trouve forcément avant nous
|
||||
{
|
||||
m_impl->joints[i].SetParent(m_impl->joints[j]); // Oui, tout ça pour ça
|
||||
break;
|
||||
if (parent == &skeleton.m_impl->joints[j]) // A-t-on trouvé le parent ?
|
||||
{
|
||||
m_impl->joints[i].SetParent(m_impl->joints[j]); // Oui, tout ça pour ça
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void NzSkeleton::InvalidateJoints()
|
||||
{
|
||||
m_impl->aabbUpdated = false;
|
||||
|
||||
OnSkeletonJointsInvalidated(this);
|
||||
}
|
||||
|
||||
void NzSkeleton::InvalidateJointMap()
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!m_impl)
|
||||
void Skeleton::InvalidateJoints()
|
||||
{
|
||||
NazaraError("Invalid skeleton");
|
||||
return;
|
||||
m_impl->aabbUpdated = false;
|
||||
|
||||
OnSkeletonJointsInvalidated(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->jointMapUpdated = false;
|
||||
}
|
||||
|
||||
void NzSkeleton::UpdateJointMap() const
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!m_impl)
|
||||
void Skeleton::InvalidateJointMap()
|
||||
{
|
||||
NazaraError("Invalid skeleton");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->jointMap.clear();
|
||||
for (unsigned int i = 0; i < m_impl->joints.size(); ++i)
|
||||
{
|
||||
NzString name = m_impl->joints[i].GetName();
|
||||
if (!name.IsEmpty())
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!m_impl)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
auto it = m_impl->jointMap.find(name);
|
||||
if (it != m_impl->jointMap.end())
|
||||
{
|
||||
NazaraWarning("Joint name \"" + name + "\" is already present in joint map for joint #" + NzString::Number(it->second));
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->jointMap[name] = i;
|
||||
NazaraError("Invalid skeleton");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->jointMapUpdated = false;
|
||||
}
|
||||
|
||||
m_impl->jointMapUpdated = true;
|
||||
}
|
||||
|
||||
bool NzSkeleton::Initialize()
|
||||
{
|
||||
if (!NzSkeletonLibrary::Initialize())
|
||||
void Skeleton::UpdateJointMap() const
|
||||
{
|
||||
NazaraError("Failed to initialise library");
|
||||
return false;
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Invalid skeleton");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->jointMap.clear();
|
||||
for (unsigned int i = 0; i < m_impl->joints.size(); ++i)
|
||||
{
|
||||
String name = m_impl->joints[i].GetName();
|
||||
if (!name.IsEmpty())
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
auto it = m_impl->jointMap.find(name);
|
||||
if (it != m_impl->jointMap.end())
|
||||
{
|
||||
NazaraWarning("Joint name \"" + name + "\" is already present in joint map for joint #" + String::Number(it->second));
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->jointMap[name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
m_impl->jointMapUpdated = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Skeleton::Initialize()
|
||||
{
|
||||
if (!SkeletonLibrary::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialise library");
|
||||
return false;
|
||||
}
|
||||
|
||||
void NzSkeleton::Uninitialize()
|
||||
{
|
||||
NzSkeletonLibrary::Uninitialize();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
NzSkeletonLibrary::LibraryMap NzSkeleton::s_library;
|
||||
void Skeleton::Uninitialize()
|
||||
{
|
||||
SkeletonLibrary::Uninitialize();
|
||||
}
|
||||
|
||||
SkeletonLibrary::LibraryMap Skeleton::s_library;
|
||||
}
|
||||
|
||||
@@ -9,92 +9,95 @@
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzSoftwareBuffer::NzSoftwareBuffer(NzBuffer* parent, nzBufferType type) :
|
||||
m_type(type)
|
||||
namespace Nz
|
||||
{
|
||||
NazaraUnused(parent);
|
||||
}
|
||||
|
||||
NzSoftwareBuffer::~NzSoftwareBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
bool NzSoftwareBuffer::Create(unsigned int size, nzBufferUsage usage)
|
||||
{
|
||||
NazaraUnused(usage);
|
||||
|
||||
// Cette allocation est protégée car sa taille dépend directement de paramètres utilisateurs
|
||||
try
|
||||
SoftwareBuffer::SoftwareBuffer(Buffer* parent, BufferType type) :
|
||||
m_type(type)
|
||||
{
|
||||
m_buffer = new nzUInt8[size];
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
NazaraError("Failed to allocate software buffer (" + NzString(e.what()) + ')');
|
||||
return false;
|
||||
NazaraUnused(parent);
|
||||
}
|
||||
|
||||
m_mapped = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzSoftwareBuffer::Destroy()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
bool NzSoftwareBuffer::Fill(const void* data, unsigned int offset, unsigned int size, bool forceDiscard)
|
||||
{
|
||||
NazaraUnused(forceDiscard);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_mapped)
|
||||
SoftwareBuffer::~SoftwareBuffer()
|
||||
{
|
||||
NazaraError("Buffer already mapped");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::memcpy(&m_buffer[offset], data, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzSoftwareBuffer::IsHardware() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void* NzSoftwareBuffer::Map(nzBufferAccess access, unsigned int offset, unsigned int size)
|
||||
{
|
||||
NazaraUnused(access);
|
||||
NazaraUnused(size);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_mapped)
|
||||
bool SoftwareBuffer::Create(unsigned int size, BufferUsage usage)
|
||||
{
|
||||
NazaraError("Buffer already mapped");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
NazaraUnused(usage);
|
||||
|
||||
m_mapped = true;
|
||||
// Cette allocation est protégée car sa taille dépend directement de paramètres utilisateurs
|
||||
try
|
||||
{
|
||||
m_buffer = new UInt8[size];
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
NazaraError("Failed to allocate software buffer (" + String(e.what()) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
return &m_buffer[offset];
|
||||
}
|
||||
m_mapped = false;
|
||||
|
||||
bool NzSoftwareBuffer::Unmap()
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_mapped)
|
||||
{
|
||||
NazaraError("Buffer not mapped");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_mapped = false;
|
||||
void SoftwareBuffer::Destroy()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
return true;
|
||||
bool SoftwareBuffer::Fill(const void* data, unsigned int offset, unsigned int size, bool forceDiscard)
|
||||
{
|
||||
NazaraUnused(forceDiscard);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_mapped)
|
||||
{
|
||||
NazaraError("Buffer already mapped");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::memcpy(&m_buffer[offset], data, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoftwareBuffer::IsHardware() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void* SoftwareBuffer::Map(BufferAccess access, unsigned int offset, unsigned int size)
|
||||
{
|
||||
NazaraUnused(access);
|
||||
NazaraUnused(size);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_mapped)
|
||||
{
|
||||
NazaraError("Buffer already mapped");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_mapped = true;
|
||||
|
||||
return &m_buffer[offset];
|
||||
}
|
||||
|
||||
bool SoftwareBuffer::Unmap()
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_mapped)
|
||||
{
|
||||
NazaraError("Buffer not mapped");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_mapped = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,26 +10,29 @@
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <Nazara/Utility/AbstractBuffer.hpp>
|
||||
|
||||
class NzSoftwareBuffer : public NzAbstractBuffer
|
||||
namespace Nz
|
||||
{
|
||||
public:
|
||||
NzSoftwareBuffer(NzBuffer* parent, nzBufferType type);
|
||||
~NzSoftwareBuffer();
|
||||
class SoftwareBuffer : public AbstractBuffer
|
||||
{
|
||||
public:
|
||||
SoftwareBuffer(Buffer* parent, BufferType type);
|
||||
~SoftwareBuffer();
|
||||
|
||||
bool Create(unsigned int size, nzBufferUsage usage = nzBufferUsage_Static);
|
||||
void Destroy();
|
||||
bool Create(unsigned int size, BufferUsage usage = BufferUsage_Static);
|
||||
void Destroy();
|
||||
|
||||
bool Fill(const void* data, unsigned int offset, unsigned int size, bool forceDiscard);
|
||||
bool Fill(const void* data, unsigned int offset, unsigned int size, bool forceDiscard);
|
||||
|
||||
bool IsHardware() const;
|
||||
bool IsHardware() const;
|
||||
|
||||
void* Map(nzBufferAccess access, unsigned int offset = 0, unsigned int size = 0);
|
||||
bool Unmap();
|
||||
void* Map(BufferAccess access, unsigned int offset = 0, unsigned int size = 0);
|
||||
bool Unmap();
|
||||
|
||||
private:
|
||||
nzBufferType m_type;
|
||||
nzUInt8* m_buffer;
|
||||
bool m_mapped;
|
||||
};
|
||||
private:
|
||||
BufferType m_type;
|
||||
UInt8* m_buffer;
|
||||
bool m_mapped;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NAZARA_SOFTWAREBUFFER_HPP
|
||||
|
||||
@@ -11,116 +11,119 @@
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzStaticMesh::NzStaticMesh(const NzMesh* parent) :
|
||||
NzSubMesh(parent)
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
NzStaticMesh::~NzStaticMesh()
|
||||
{
|
||||
OnStaticMeshRelease(this);
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void NzStaticMesh::Center()
|
||||
{
|
||||
NzVector3f offset(m_aabb.x + m_aabb.width/2.f, m_aabb.y + m_aabb.height/2.f, m_aabb.z + m_aabb.depth/2.f);
|
||||
|
||||
NzVertexMapper mapper(m_vertexBuffer);
|
||||
NzSparsePtr<NzVector3f> position = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Position);
|
||||
|
||||
unsigned int vertexCount = m_vertexBuffer->GetVertexCount();
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
*position++ -= offset;
|
||||
|
||||
m_aabb.x -= offset.x;
|
||||
m_aabb.y -= offset.y;
|
||||
m_aabb.z -= offset.z;
|
||||
}
|
||||
|
||||
bool NzStaticMesh::Create(NzVertexBuffer* vertexBuffer)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!vertexBuffer)
|
||||
StaticMesh::StaticMesh(const Mesh* parent) :
|
||||
SubMesh(parent)
|
||||
{
|
||||
}
|
||||
|
||||
StaticMesh::~StaticMesh()
|
||||
{
|
||||
OnStaticMeshRelease(this);
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void StaticMesh::Center()
|
||||
{
|
||||
Vector3f offset(m_aabb.x + m_aabb.width/2.f, m_aabb.y + m_aabb.height/2.f, m_aabb.z + m_aabb.depth/2.f);
|
||||
|
||||
VertexMapper mapper(m_vertexBuffer);
|
||||
SparsePtr<Vector3f> position = mapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
|
||||
|
||||
unsigned int vertexCount = m_vertexBuffer->GetVertexCount();
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
*position++ -= offset;
|
||||
|
||||
m_aabb.x -= offset.x;
|
||||
m_aabb.y -= offset.y;
|
||||
m_aabb.z -= offset.z;
|
||||
}
|
||||
|
||||
bool StaticMesh::Create(VertexBuffer* vertexBuffer)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!vertexBuffer)
|
||||
{
|
||||
NazaraError("Invalid vertex buffer");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_vertexBuffer = vertexBuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
void StaticMesh::Destroy()
|
||||
{
|
||||
if (m_vertexBuffer)
|
||||
{
|
||||
OnStaticMeshDestroy(this);
|
||||
|
||||
m_indexBuffer.Reset();
|
||||
m_vertexBuffer.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool StaticMesh::GenerateAABB()
|
||||
{
|
||||
// On lock le buffer pour itérer sur toutes les positions et composer notre AABB
|
||||
VertexMapper mapper(m_vertexBuffer, BufferAccess_ReadOnly);
|
||||
m_aabb = ComputeAABB(mapper.GetComponentPtr<const Vector3f>(VertexComponent_Position), m_vertexBuffer->GetVertexCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Boxf& StaticMesh::GetAABB() const
|
||||
{
|
||||
return m_aabb;
|
||||
}
|
||||
|
||||
AnimationType StaticMesh::GetAnimationType() const
|
||||
{
|
||||
return AnimationType_Static;
|
||||
}
|
||||
|
||||
const IndexBuffer* StaticMesh::GetIndexBuffer() const
|
||||
{
|
||||
return m_indexBuffer;
|
||||
}
|
||||
|
||||
VertexBuffer* StaticMesh::GetVertexBuffer()
|
||||
{
|
||||
return m_vertexBuffer;
|
||||
}
|
||||
|
||||
const VertexBuffer* StaticMesh::GetVertexBuffer() const
|
||||
{
|
||||
return m_vertexBuffer;
|
||||
}
|
||||
|
||||
unsigned int StaticMesh::GetVertexCount() const
|
||||
{
|
||||
return m_vertexBuffer->GetVertexCount();
|
||||
}
|
||||
|
||||
bool StaticMesh::IsAnimated() const
|
||||
{
|
||||
NazaraError("Invalid vertex buffer");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_vertexBuffer = vertexBuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzStaticMesh::Destroy()
|
||||
{
|
||||
if (m_vertexBuffer)
|
||||
bool StaticMesh::IsValid() const
|
||||
{
|
||||
OnStaticMeshDestroy(this);
|
||||
|
||||
m_indexBuffer.Reset();
|
||||
m_vertexBuffer.Reset();
|
||||
return m_vertexBuffer != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool NzStaticMesh::GenerateAABB()
|
||||
{
|
||||
// On lock le buffer pour itérer sur toutes les positions et composer notre AABB
|
||||
NzVertexMapper mapper(m_vertexBuffer, nzBufferAccess_ReadOnly);
|
||||
m_aabb = NzComputeAABB(mapper.GetComponentPtr<const NzVector3f>(nzVertexComponent_Position), m_vertexBuffer->GetVertexCount());
|
||||
void StaticMesh::SetAABB(const Boxf& aabb)
|
||||
{
|
||||
m_aabb = aabb;
|
||||
}
|
||||
|
||||
return true;
|
||||
void StaticMesh::SetIndexBuffer(const IndexBuffer* indexBuffer)
|
||||
{
|
||||
m_indexBuffer = indexBuffer;
|
||||
}
|
||||
|
||||
const NzBoxf& NzStaticMesh::GetAABB() const
|
||||
{
|
||||
return m_aabb;
|
||||
}
|
||||
|
||||
nzAnimationType NzStaticMesh::GetAnimationType() const
|
||||
{
|
||||
return nzAnimationType_Static;
|
||||
}
|
||||
|
||||
const NzIndexBuffer* NzStaticMesh::GetIndexBuffer() const
|
||||
{
|
||||
return m_indexBuffer;
|
||||
}
|
||||
|
||||
NzVertexBuffer* NzStaticMesh::GetVertexBuffer()
|
||||
{
|
||||
return m_vertexBuffer;
|
||||
}
|
||||
|
||||
const NzVertexBuffer* NzStaticMesh::GetVertexBuffer() const
|
||||
{
|
||||
return m_vertexBuffer;
|
||||
}
|
||||
|
||||
unsigned int NzStaticMesh::GetVertexCount() const
|
||||
{
|
||||
return m_vertexBuffer->GetVertexCount();
|
||||
}
|
||||
|
||||
bool NzStaticMesh::IsAnimated() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NzStaticMesh::IsValid() const
|
||||
{
|
||||
return m_vertexBuffer != nullptr;
|
||||
}
|
||||
|
||||
void NzStaticMesh::SetAABB(const NzBoxf& aabb)
|
||||
{
|
||||
m_aabb = aabb;
|
||||
}
|
||||
|
||||
void NzStaticMesh::SetIndexBuffer(const NzIndexBuffer* indexBuffer)
|
||||
{
|
||||
m_indexBuffer = indexBuffer;
|
||||
}
|
||||
|
||||
@@ -12,199 +12,202 @@
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzSubMesh::NzSubMesh(const NzMesh* parent) :
|
||||
NzRefCounted(false), // Un SubMesh n'est pas persistant par défaut
|
||||
m_primitiveMode(nzPrimitiveMode_TriangleList),
|
||||
m_parent(parent),
|
||||
m_matIndex(0)
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
NzSubMesh::~NzSubMesh()
|
||||
{
|
||||
OnSubMeshRelease(this);
|
||||
}
|
||||
|
||||
void NzSubMesh::GenerateNormals()
|
||||
{
|
||||
NzVertexMapper mapper(this);
|
||||
unsigned int vertexCount = GetVertexCount();
|
||||
|
||||
NzSparsePtr<NzVector3f> normals = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Normal);
|
||||
NzSparsePtr<NzVector3f> positions = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Position);
|
||||
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
normals[i].MakeZero();
|
||||
|
||||
NzTriangleIterator iterator(this);
|
||||
do
|
||||
SubMesh::SubMesh(const Mesh* parent) :
|
||||
RefCounted(false), // Un SubMesh n'est pas persistant par défaut
|
||||
m_primitiveMode(PrimitiveMode_TriangleList),
|
||||
m_parent(parent),
|
||||
m_matIndex(0)
|
||||
{
|
||||
NzVector3f pos0 = positions[iterator[0]];
|
||||
|
||||
NzVector3f dv[2];
|
||||
dv[0] = positions[iterator[1]] - pos0;
|
||||
dv[1] = positions[iterator[2]] - pos0;
|
||||
|
||||
NzVector3f normal = dv[0].CrossProduct(dv[1]);
|
||||
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
normals[iterator[i]] += normal;
|
||||
}
|
||||
while (iterator.Advance());
|
||||
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
normals[i].Normalize();
|
||||
}
|
||||
|
||||
void NzSubMesh::GenerateNormalsAndTangents()
|
||||
{
|
||||
NzVertexMapper mapper(this);
|
||||
unsigned int vertexCount = GetVertexCount();
|
||||
|
||||
NzSparsePtr<NzVector3f> normals = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Normal);
|
||||
NzSparsePtr<NzVector3f> positions = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Position);
|
||||
NzSparsePtr<NzVector3f> tangents = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Tangent);
|
||||
NzSparsePtr<NzVector2f> texCoords = mapper.GetComponentPtr<NzVector2f>(nzVertexComponent_TexCoord);
|
||||
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
normals[i].MakeZero();
|
||||
tangents[i].MakeZero();
|
||||
}
|
||||
|
||||
NzTriangleIterator iterator(this);
|
||||
do
|
||||
SubMesh::~SubMesh()
|
||||
{
|
||||
NzVector3f pos0 = positions[iterator[0]];
|
||||
OnSubMeshRelease(this);
|
||||
}
|
||||
|
||||
NzVector3f dv[2];
|
||||
dv[0] = positions[iterator[1]] - pos0;
|
||||
dv[1] = positions[iterator[2]] - pos0;
|
||||
void SubMesh::GenerateNormals()
|
||||
{
|
||||
VertexMapper mapper(this);
|
||||
unsigned int vertexCount = GetVertexCount();
|
||||
|
||||
NzVector3f normal = dv[0].CrossProduct(dv[1]);
|
||||
SparsePtr<Vector3f> normals = mapper.GetComponentPtr<Vector3f>(VertexComponent_Normal);
|
||||
SparsePtr<Vector3f> positions = mapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
|
||||
|
||||
NzVector2f uv0 = texCoords[iterator[0]];
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
normals[i].MakeZero();
|
||||
|
||||
NzVector2f duv[2];
|
||||
duv[0] = texCoords[iterator[1]] - uv0;
|
||||
duv[1] = texCoords[iterator[2]] - uv0;
|
||||
|
||||
float coef = 1.f / (duv[0].x*duv[1].y - duv[1].x*duv[0].y);
|
||||
|
||||
NzVector3f tangent;
|
||||
tangent.x = coef * (dv[0].x*duv[1].y + dv[1].x*(-duv[0].y));
|
||||
tangent.y = coef * (dv[0].y*duv[1].y + dv[1].y*(-duv[0].y));
|
||||
tangent.z = coef * (dv[0].z*duv[1].y + dv[1].z*(-duv[0].y));
|
||||
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
TriangleIterator iterator(this);
|
||||
do
|
||||
{
|
||||
nzUInt32 index = iterator[i];
|
||||
normals[index] += normal;
|
||||
tangents[index] += tangent;
|
||||
Vector3f pos0 = positions[iterator[0]];
|
||||
|
||||
Vector3f dv[2];
|
||||
dv[0] = positions[iterator[1]] - pos0;
|
||||
dv[1] = positions[iterator[2]] - pos0;
|
||||
|
||||
Vector3f normal = dv[0].CrossProduct(dv[1]);
|
||||
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
normals[iterator[i]] += normal;
|
||||
}
|
||||
while (iterator.Advance());
|
||||
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
normals[i].Normalize();
|
||||
}
|
||||
|
||||
void SubMesh::GenerateNormalsAndTangents()
|
||||
{
|
||||
VertexMapper mapper(this);
|
||||
unsigned int vertexCount = GetVertexCount();
|
||||
|
||||
SparsePtr<Vector3f> normals = mapper.GetComponentPtr<Vector3f>(VertexComponent_Normal);
|
||||
SparsePtr<Vector3f> positions = mapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
|
||||
SparsePtr<Vector3f> tangents = mapper.GetComponentPtr<Vector3f>(VertexComponent_Tangent);
|
||||
SparsePtr<Vector2f> texCoords = mapper.GetComponentPtr<Vector2f>(VertexComponent_TexCoord);
|
||||
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
normals[i].MakeZero();
|
||||
tangents[i].MakeZero();
|
||||
}
|
||||
|
||||
TriangleIterator iterator(this);
|
||||
do
|
||||
{
|
||||
Vector3f pos0 = positions[iterator[0]];
|
||||
|
||||
Vector3f dv[2];
|
||||
dv[0] = positions[iterator[1]] - pos0;
|
||||
dv[1] = positions[iterator[2]] - pos0;
|
||||
|
||||
Vector3f normal = dv[0].CrossProduct(dv[1]);
|
||||
|
||||
Vector2f uv0 = texCoords[iterator[0]];
|
||||
|
||||
Vector2f duv[2];
|
||||
duv[0] = texCoords[iterator[1]] - uv0;
|
||||
duv[1] = texCoords[iterator[2]] - uv0;
|
||||
|
||||
float coef = 1.f / (duv[0].x*duv[1].y - duv[1].x*duv[0].y);
|
||||
|
||||
Vector3f tangent;
|
||||
tangent.x = coef * (dv[0].x*duv[1].y + dv[1].x*(-duv[0].y));
|
||||
tangent.y = coef * (dv[0].y*duv[1].y + dv[1].y*(-duv[0].y));
|
||||
tangent.z = coef * (dv[0].z*duv[1].y + dv[1].z*(-duv[0].y));
|
||||
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
{
|
||||
UInt32 index = iterator[i];
|
||||
normals[index] += normal;
|
||||
tangents[index] += tangent;
|
||||
}
|
||||
}
|
||||
while (iterator.Advance());
|
||||
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
normals[i].Normalize();
|
||||
tangents[i].Normalize();
|
||||
}
|
||||
}
|
||||
while (iterator.Advance());
|
||||
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
void SubMesh::GenerateTangents()
|
||||
{
|
||||
normals[i].Normalize();
|
||||
tangents[i].Normalize();
|
||||
}
|
||||
}
|
||||
VertexMapper mapper(this);
|
||||
|
||||
void NzSubMesh::GenerateTangents()
|
||||
{
|
||||
NzVertexMapper mapper(this);
|
||||
SparsePtr<Vector3f> normals = mapper.GetComponentPtr<Vector3f>(VertexComponent_Normal);
|
||||
SparsePtr<Vector3f> positions = mapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
|
||||
SparsePtr<Vector3f> tangents = mapper.GetComponentPtr<Vector3f>(VertexComponent_Tangent);
|
||||
SparsePtr<Vector2f> texCoords = mapper.GetComponentPtr<Vector2f>(VertexComponent_TexCoord);
|
||||
|
||||
NzSparsePtr<NzVector3f> normals = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Normal);
|
||||
NzSparsePtr<NzVector3f> positions = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Position);
|
||||
NzSparsePtr<NzVector3f> tangents = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Tangent);
|
||||
NzSparsePtr<NzVector2f> texCoords = mapper.GetComponentPtr<NzVector2f>(nzVertexComponent_TexCoord);
|
||||
|
||||
NzTriangleIterator iterator(this);
|
||||
do
|
||||
{
|
||||
NzVector3f pos0 = positions[iterator[0]];
|
||||
NzVector2f uv0 = texCoords[iterator[0]];
|
||||
NzVector2f uv1 = texCoords[iterator[1]];
|
||||
NzVector2f uv2 = texCoords[iterator[2]];
|
||||
|
||||
NzVector3f dv[2];
|
||||
dv[0] = positions[iterator[1]] - pos0;
|
||||
dv[1] = positions[iterator[2]] - pos0;
|
||||
|
||||
float ds[2];
|
||||
ds[0] = uv1.x - uv0.x;
|
||||
ds[1] = uv2.x - uv0.x;
|
||||
|
||||
NzVector3f ppt;
|
||||
ppt.x = ds[0]*dv[1].x - dv[0].x*ds[1];
|
||||
ppt.y = ds[0]*dv[1].y - dv[0].y*ds[1];
|
||||
ppt.z = ds[0]*dv[1].z - dv[0].z*ds[1];
|
||||
ppt.Normalize();
|
||||
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
TriangleIterator iterator(this);
|
||||
do
|
||||
{
|
||||
NzVector3f normal = normals[iterator[i]];
|
||||
float d = ppt.DotProduct(normal);
|
||||
Vector3f pos0 = positions[iterator[0]];
|
||||
Vector2f uv0 = texCoords[iterator[0]];
|
||||
Vector2f uv1 = texCoords[iterator[1]];
|
||||
Vector2f uv2 = texCoords[iterator[2]];
|
||||
|
||||
tangents[iterator[i]] = ppt - (d * normal);
|
||||
Vector3f dv[2];
|
||||
dv[0] = positions[iterator[1]] - pos0;
|
||||
dv[1] = positions[iterator[2]] - pos0;
|
||||
|
||||
float ds[2];
|
||||
ds[0] = uv1.x - uv0.x;
|
||||
ds[1] = uv2.x - uv0.x;
|
||||
|
||||
Vector3f ppt;
|
||||
ppt.x = ds[0]*dv[1].x - dv[0].x*ds[1];
|
||||
ppt.y = ds[0]*dv[1].y - dv[0].y*ds[1];
|
||||
ppt.z = ds[0]*dv[1].z - dv[0].z*ds[1];
|
||||
ppt.Normalize();
|
||||
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
{
|
||||
Vector3f normal = normals[iterator[i]];
|
||||
float d = ppt.DotProduct(normal);
|
||||
|
||||
tangents[iterator[i]] = ppt - (d * normal);
|
||||
}
|
||||
}
|
||||
while (iterator.Advance());
|
||||
}
|
||||
while (iterator.Advance());
|
||||
}
|
||||
|
||||
const NzMesh* NzSubMesh::GetParent() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
nzPrimitiveMode NzSubMesh::GetPrimitiveMode() const
|
||||
{
|
||||
return m_primitiveMode;
|
||||
}
|
||||
|
||||
unsigned int NzSubMesh::GetTriangleCount() const
|
||||
{
|
||||
const NzIndexBuffer* indexBuffer = GetIndexBuffer();
|
||||
unsigned int indexCount;
|
||||
if (indexBuffer)
|
||||
indexCount = indexBuffer->GetIndexCount();
|
||||
else
|
||||
indexCount = GetVertexCount();
|
||||
|
||||
switch (m_primitiveMode)
|
||||
const Mesh* SubMesh::GetParent() const
|
||||
{
|
||||
case nzPrimitiveMode_LineList:
|
||||
case nzPrimitiveMode_LineStrip:
|
||||
case nzPrimitiveMode_PointList:
|
||||
return 0;
|
||||
|
||||
case nzPrimitiveMode_TriangleFan:
|
||||
return (indexCount - 1) / 2;
|
||||
|
||||
case nzPrimitiveMode_TriangleList:
|
||||
return indexCount / 3;
|
||||
|
||||
case nzPrimitiveMode_TriangleStrip:
|
||||
return indexCount - 2;
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
NazaraError("Primitive mode not handled (0x" + NzString::Number(m_primitiveMode, 16) + ')');
|
||||
return 0;
|
||||
}
|
||||
PrimitiveMode SubMesh::GetPrimitiveMode() const
|
||||
{
|
||||
return m_primitiveMode;
|
||||
}
|
||||
|
||||
unsigned int NzSubMesh::GetMaterialIndex() const
|
||||
{
|
||||
return m_matIndex;
|
||||
}
|
||||
unsigned int SubMesh::GetTriangleCount() const
|
||||
{
|
||||
const IndexBuffer* indexBuffer = GetIndexBuffer();
|
||||
unsigned int indexCount;
|
||||
if (indexBuffer)
|
||||
indexCount = indexBuffer->GetIndexCount();
|
||||
else
|
||||
indexCount = GetVertexCount();
|
||||
|
||||
void NzSubMesh::SetPrimitiveMode(nzPrimitiveMode mode)
|
||||
{
|
||||
m_primitiveMode = mode;
|
||||
}
|
||||
switch (m_primitiveMode)
|
||||
{
|
||||
case PrimitiveMode_LineList:
|
||||
case PrimitiveMode_LineStrip:
|
||||
case PrimitiveMode_PointList:
|
||||
return 0;
|
||||
|
||||
void NzSubMesh::SetMaterialIndex(unsigned int matIndex)
|
||||
{
|
||||
m_matIndex = matIndex;
|
||||
case PrimitiveMode_TriangleFan:
|
||||
return (indexCount - 1) / 2;
|
||||
|
||||
case PrimitiveMode_TriangleList:
|
||||
return indexCount / 3;
|
||||
|
||||
case PrimitiveMode_TriangleStrip:
|
||||
return indexCount - 2;
|
||||
}
|
||||
|
||||
NazaraError("Primitive mode not handled (0x" + String::Number(m_primitiveMode, 16) + ')');
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int SubMesh::GetMaterialIndex() const
|
||||
{
|
||||
return m_matIndex;
|
||||
}
|
||||
|
||||
void SubMesh::SetPrimitiveMode(PrimitiveMode mode)
|
||||
{
|
||||
m_primitiveMode = mode;
|
||||
}
|
||||
|
||||
void SubMesh::SetMaterialIndex(unsigned int matIndex)
|
||||
{
|
||||
m_matIndex = matIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,75 +6,78 @@
|
||||
#include <Nazara/Utility/SubMesh.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzTriangleIterator::NzTriangleIterator(nzPrimitiveMode primitiveMode, const NzIndexBuffer* indexBuffer) :
|
||||
m_primitiveMode(primitiveMode),
|
||||
m_indexMapper(indexBuffer, nzBufferAccess_ReadOnly)
|
||||
namespace Nz
|
||||
{
|
||||
m_currentIndex = 3;
|
||||
m_triangleIndices[0] = m_indexMapper.Get(0);
|
||||
m_triangleIndices[1] = m_indexMapper.Get(1);
|
||||
m_triangleIndices[2] = m_indexMapper.Get(2);
|
||||
|
||||
m_indexCount = indexBuffer->GetIndexCount();
|
||||
}
|
||||
|
||||
NzTriangleIterator::NzTriangleIterator(NzSubMesh* subMesh) :
|
||||
NzTriangleIterator(subMesh->GetPrimitiveMode(), subMesh->GetIndexBuffer())
|
||||
{
|
||||
}
|
||||
|
||||
bool NzTriangleIterator::Advance()
|
||||
{
|
||||
if (m_currentIndex >= m_indexCount)
|
||||
TriangleIterator::TriangleIterator(PrimitiveMode primitiveMode, const IndexBuffer* indexBuffer) :
|
||||
m_primitiveMode(primitiveMode),
|
||||
m_indexMapper(indexBuffer, BufferAccess_ReadOnly)
|
||||
{
|
||||
Unmap();
|
||||
return false;
|
||||
m_currentIndex = 3;
|
||||
m_triangleIndices[0] = m_indexMapper.Get(0);
|
||||
m_triangleIndices[1] = m_indexMapper.Get(1);
|
||||
m_triangleIndices[2] = m_indexMapper.Get(2);
|
||||
|
||||
m_indexCount = indexBuffer->GetIndexCount();
|
||||
}
|
||||
|
||||
switch (m_primitiveMode)
|
||||
TriangleIterator::TriangleIterator(SubMesh* subMesh) :
|
||||
TriangleIterator(subMesh->GetPrimitiveMode(), subMesh->GetIndexBuffer())
|
||||
{
|
||||
case nzPrimitiveMode_TriangleFan:
|
||||
m_triangleIndices[1] = m_indexMapper.Get(m_currentIndex++);
|
||||
m_triangleIndices[2] = m_indexMapper.Get(m_currentIndex++);
|
||||
break;
|
||||
}
|
||||
|
||||
case nzPrimitiveMode_TriangleList:
|
||||
m_triangleIndices[0] = m_indexMapper.Get(m_currentIndex++);
|
||||
m_triangleIndices[1] = m_indexMapper.Get(m_currentIndex++);
|
||||
m_triangleIndices[2] = m_indexMapper.Get(m_currentIndex++);
|
||||
break;
|
||||
|
||||
case nzPrimitiveMode_TriangleStrip:
|
||||
m_triangleIndices[2] = m_indexMapper.Get(m_currentIndex++);
|
||||
m_triangleIndices[0] = m_triangleIndices[1];
|
||||
m_triangleIndices[1] = m_triangleIndices[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
bool TriangleIterator::Advance()
|
||||
{
|
||||
if (m_currentIndex >= m_indexCount)
|
||||
{
|
||||
Unmap();
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (m_primitiveMode)
|
||||
{
|
||||
case PrimitiveMode_TriangleFan:
|
||||
m_triangleIndices[1] = m_indexMapper.Get(m_currentIndex++);
|
||||
m_triangleIndices[2] = m_indexMapper.Get(m_currentIndex++);
|
||||
break;
|
||||
|
||||
case PrimitiveMode_TriangleList:
|
||||
m_triangleIndices[0] = m_indexMapper.Get(m_currentIndex++);
|
||||
m_triangleIndices[1] = m_indexMapper.Get(m_currentIndex++);
|
||||
m_triangleIndices[2] = m_indexMapper.Get(m_currentIndex++);
|
||||
break;
|
||||
|
||||
case PrimitiveMode_TriangleStrip:
|
||||
m_triangleIndices[2] = m_indexMapper.Get(m_currentIndex++);
|
||||
m_triangleIndices[0] = m_triangleIndices[1];
|
||||
m_triangleIndices[1] = m_triangleIndices[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nzUInt32 NzTriangleIterator::operator[](unsigned int i) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (i >= 3)
|
||||
UInt32 TriangleIterator::operator[](unsigned int i) const
|
||||
{
|
||||
NzStringStream ss;
|
||||
ss << "Index out of range: (" << i << " >= 3)";
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (i >= 3)
|
||||
{
|
||||
StringStream ss;
|
||||
ss << "Index out of range: (" << i << " >= 3)";
|
||||
|
||||
NazaraError(ss);
|
||||
throw std::domain_error(ss.ToString());
|
||||
NazaraError(ss);
|
||||
throw std::domain_error(ss.ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_triangleIndices[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_triangleIndices[i];
|
||||
}
|
||||
|
||||
void NzTriangleIterator::Unmap()
|
||||
{
|
||||
// Peut très bien être appellé plusieurs fois de suite, seul le premier appel sera pris en compte
|
||||
m_indexMapper.Unmap();
|
||||
void TriangleIterator::Unmap()
|
||||
{
|
||||
// Peut très bien être appellé plusieurs fois de suite, seul le premier appel sera pris en compte
|
||||
m_indexMapper.Unmap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,187 +28,190 @@
|
||||
#include <Nazara/Utility/Formats/STBLoader.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
bool NzUtility::Initialize()
|
||||
namespace Nz
|
||||
{
|
||||
if (s_moduleReferenceCounter > 0)
|
||||
bool Utility::Initialize()
|
||||
{
|
||||
if (s_moduleReferenceCounter > 0)
|
||||
{
|
||||
s_moduleReferenceCounter++;
|
||||
return true; // Déjà initialisé
|
||||
}
|
||||
|
||||
// Initialisation des dépendances
|
||||
if (!Core::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize core module");
|
||||
return false;
|
||||
}
|
||||
|
||||
s_moduleReferenceCounter++;
|
||||
return true; // Déjà initialisé
|
||||
|
||||
// Initialisation du module
|
||||
CallOnExit onExit(Utility::Uninitialize);
|
||||
|
||||
if (!Animation::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize animations");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Buffer::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize buffers");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Font::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize fonts");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Image::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize images");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Mesh::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize meshes");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PixelFormat::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize pixel formats");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Skeleton::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize skeletons");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!VertexDeclaration::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize vertex declarations");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Window::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize window's system");
|
||||
return false;
|
||||
}
|
||||
|
||||
// On enregistre les loaders pour les extensions
|
||||
// Il s'agit ici d'une liste LIFO, le dernier loader enregistré possède la priorité
|
||||
|
||||
/// Loaders génériques
|
||||
// Font
|
||||
Loaders::RegisterFreeType();
|
||||
|
||||
// Image
|
||||
Loaders::RegisterSTB(); // Loader générique (STB)
|
||||
|
||||
/// Loaders spécialisés
|
||||
// Animation
|
||||
Loaders::RegisterMD5Anim(); // Loader de fichiers .md5anim (v10)
|
||||
|
||||
// Mesh
|
||||
Loaders::RegisterMD2(); // Loader de fichiers .md2 (v8)
|
||||
Loaders::RegisterMD5Mesh(); // Loader de fichiers .md5mesh (v10)
|
||||
|
||||
// Image
|
||||
Loaders::RegisterPCX(); // Loader de fichiers .pcx (1, 4, 8, 24 bits)
|
||||
|
||||
onExit.Reset();
|
||||
|
||||
NazaraNotice("Initialized: Utility module");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialisation des dépendances
|
||||
if (!NzCore::Initialize())
|
||||
bool Utility::IsInitialized()
|
||||
{
|
||||
NazaraError("Failed to initialize core module");
|
||||
return false;
|
||||
return s_moduleReferenceCounter != 0;
|
||||
}
|
||||
|
||||
s_moduleReferenceCounter++;
|
||||
|
||||
// Initialisation du module
|
||||
NzCallOnExit onExit(NzUtility::Uninitialize);
|
||||
|
||||
if (!NzAnimation::Initialize())
|
||||
void Utility::Uninitialize()
|
||||
{
|
||||
NazaraError("Failed to initialize animations");
|
||||
return false;
|
||||
if (s_moduleReferenceCounter != 1)
|
||||
{
|
||||
// Le module est soit encore utilisé, soit pas initialisé
|
||||
if (s_moduleReferenceCounter > 1)
|
||||
s_moduleReferenceCounter--;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Libération du module
|
||||
s_moduleReferenceCounter = 0;
|
||||
|
||||
Loaders::UnregisterFreeType();
|
||||
Loaders::UnregisterMD2();
|
||||
Loaders::UnregisterMD5Anim();
|
||||
Loaders::UnregisterMD5Mesh();
|
||||
Loaders::UnregisterPCX();
|
||||
Loaders::UnregisterSTB();
|
||||
|
||||
Window::Uninitialize();
|
||||
VertexDeclaration::Uninitialize();
|
||||
Skeleton::Uninitialize();
|
||||
PixelFormat::Uninitialize();
|
||||
Mesh::Uninitialize();
|
||||
Image::Uninitialize();
|
||||
Font::Uninitialize();
|
||||
Buffer::Uninitialize();
|
||||
Animation::Uninitialize();
|
||||
|
||||
NazaraNotice("Uninitialized: Utility module");
|
||||
|
||||
// Libération des dépendances
|
||||
Core::Uninitialize();
|
||||
}
|
||||
|
||||
if (!NzBuffer::Initialize())
|
||||
unsigned int Utility::ComponentCount[ComponentType_Max+1] =
|
||||
{
|
||||
NazaraError("Failed to initialize buffers");
|
||||
return false;
|
||||
}
|
||||
4, // ComponentType_Color
|
||||
1, // ComponentType_Double1
|
||||
2, // ComponentType_Double2
|
||||
3, // ComponentType_Double3
|
||||
4, // ComponentType_Double4
|
||||
1, // ComponentType_Float1
|
||||
2, // ComponentType_Float2
|
||||
3, // ComponentType_Float3
|
||||
4, // ComponentType_Float4
|
||||
1, // ComponentType_Int1
|
||||
2, // ComponentType_Int2
|
||||
3, // ComponentType_Int3
|
||||
4, // ComponentType_Int4
|
||||
4 // ComponentType_Quaternion
|
||||
};
|
||||
|
||||
if (!NzFont::Initialize())
|
||||
static_assert(ComponentType_Max+1 == 14, "Component count array is incomplete");
|
||||
|
||||
std::size_t Utility::ComponentStride[ComponentType_Max+1] =
|
||||
{
|
||||
NazaraError("Failed to initialize fonts");
|
||||
return false;
|
||||
}
|
||||
4*sizeof(UInt8), // ComponentType_Color
|
||||
1*sizeof(double), // ComponentType_Double1
|
||||
2*sizeof(double), // ComponentType_Double2
|
||||
3*sizeof(double), // ComponentType_Double3
|
||||
4*sizeof(double), // ComponentType_Double4
|
||||
1*sizeof(float), // ComponentType_Float1
|
||||
2*sizeof(float), // ComponentType_Float2
|
||||
3*sizeof(float), // ComponentType_Float3
|
||||
4*sizeof(float), // ComponentType_Float4
|
||||
1*sizeof(UInt32), // ComponentType_Int1
|
||||
2*sizeof(UInt32), // ComponentType_Int2
|
||||
3*sizeof(UInt32), // ComponentType_Int3
|
||||
4*sizeof(UInt32), // ComponentType_Int4
|
||||
4*sizeof(float) // ComponentType_Quaternion
|
||||
};
|
||||
|
||||
if (!NzImage::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize images");
|
||||
return false;
|
||||
}
|
||||
static_assert(ComponentType_Max+1 == 14, "Component stride array is incomplete");
|
||||
|
||||
if (!NzMesh::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize meshes");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NzPixelFormat::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize pixel formats");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NzSkeleton::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize skeletons");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NzVertexDeclaration::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize vertex declarations");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NzWindow::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize window's system");
|
||||
return false;
|
||||
}
|
||||
|
||||
// On enregistre les loaders pour les extensions
|
||||
// Il s'agit ici d'une liste LIFO, le dernier loader enregistré possède la priorité
|
||||
|
||||
/// Loaders génériques
|
||||
// Font
|
||||
NzLoaders_FreeType_Register();
|
||||
|
||||
// Image
|
||||
NzLoaders_STB_Register(); // Loader générique (STB)
|
||||
|
||||
/// Loaders spécialisés
|
||||
// Animation
|
||||
NzLoaders_MD5Anim_Register(); // Loader de fichiers .md5anim (v10)
|
||||
|
||||
// Mesh
|
||||
NzLoaders_MD2_Register(); // Loader de fichiers .md2 (v8)
|
||||
NzLoaders_MD5Mesh_Register(); // Loader de fichiers .md5mesh (v10)
|
||||
|
||||
// Image
|
||||
NzLoaders_PCX_Register(); // Loader de fichiers .pcx (1, 4, 8, 24 bits)
|
||||
|
||||
onExit.Reset();
|
||||
|
||||
NazaraNotice("Initialized: Utility module");
|
||||
return true;
|
||||
unsigned int Utility::s_moduleReferenceCounter = 0;
|
||||
}
|
||||
|
||||
bool NzUtility::IsInitialized()
|
||||
{
|
||||
return s_moduleReferenceCounter != 0;
|
||||
}
|
||||
|
||||
void NzUtility::Uninitialize()
|
||||
{
|
||||
if (s_moduleReferenceCounter != 1)
|
||||
{
|
||||
// Le module est soit encore utilisé, soit pas initialisé
|
||||
if (s_moduleReferenceCounter > 1)
|
||||
s_moduleReferenceCounter--;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Libération du module
|
||||
s_moduleReferenceCounter = 0;
|
||||
|
||||
NzLoaders_FreeType_Unregister();
|
||||
NzLoaders_MD2_Unregister();
|
||||
NzLoaders_MD5Anim_Unregister();
|
||||
NzLoaders_MD5Mesh_Unregister();
|
||||
NzLoaders_PCX_Unregister();
|
||||
NzLoaders_STB_Unregister();
|
||||
|
||||
NzWindow::Uninitialize();
|
||||
NzVertexDeclaration::Uninitialize();
|
||||
NzSkeleton::Uninitialize();
|
||||
NzPixelFormat::Uninitialize();
|
||||
NzMesh::Uninitialize();
|
||||
NzImage::Uninitialize();
|
||||
NzFont::Uninitialize();
|
||||
NzBuffer::Uninitialize();
|
||||
NzAnimation::Uninitialize();
|
||||
|
||||
NazaraNotice("Uninitialized: Utility module");
|
||||
|
||||
// Libération des dépendances
|
||||
NzCore::Uninitialize();
|
||||
}
|
||||
|
||||
unsigned int NzUtility::ComponentCount[nzComponentType_Max+1] =
|
||||
{
|
||||
4, // nzComponentType_Color
|
||||
1, // nzComponentType_Double1
|
||||
2, // nzComponentType_Double2
|
||||
3, // nzComponentType_Double3
|
||||
4, // nzComponentType_Double4
|
||||
1, // nzComponentType_Float1
|
||||
2, // nzComponentType_Float2
|
||||
3, // nzComponentType_Float3
|
||||
4, // nzComponentType_Float4
|
||||
1, // nzComponentType_Int1
|
||||
2, // nzComponentType_Int2
|
||||
3, // nzComponentType_Int3
|
||||
4, // nzComponentType_Int4
|
||||
4 // nzComponentType_Quaternion
|
||||
};
|
||||
|
||||
static_assert(nzComponentType_Max+1 == 14, "Component count array is incomplete");
|
||||
|
||||
std::size_t NzUtility::ComponentStride[nzComponentType_Max+1] =
|
||||
{
|
||||
4*sizeof(nzUInt8), // nzComponentType_Color
|
||||
1*sizeof(double), // nzComponentType_Double1
|
||||
2*sizeof(double), // nzComponentType_Double2
|
||||
3*sizeof(double), // nzComponentType_Double3
|
||||
4*sizeof(double), // nzComponentType_Double4
|
||||
1*sizeof(float), // nzComponentType_Float1
|
||||
2*sizeof(float), // nzComponentType_Float2
|
||||
3*sizeof(float), // nzComponentType_Float3
|
||||
4*sizeof(float), // nzComponentType_Float4
|
||||
1*sizeof(nzUInt32), // nzComponentType_Int1
|
||||
2*sizeof(nzUInt32), // nzComponentType_Int2
|
||||
3*sizeof(nzUInt32), // nzComponentType_Int3
|
||||
4*sizeof(nzUInt32), // nzComponentType_Int4
|
||||
4*sizeof(float) // nzComponentType_Quaternion
|
||||
};
|
||||
|
||||
static_assert(nzComponentType_Max+1 == 14, "Component stride array is incomplete");
|
||||
|
||||
unsigned int NzUtility::s_moduleReferenceCounter = 0;
|
||||
|
||||
@@ -8,265 +8,268 @@
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzVertexBuffer::NzVertexBuffer(const NzVertexDeclaration* vertexDeclaration, NzBuffer* buffer)
|
||||
namespace Nz
|
||||
{
|
||||
NzErrorFlags(nzErrorFlag_ThrowException, true);
|
||||
Reset(vertexDeclaration, buffer);
|
||||
}
|
||||
|
||||
NzVertexBuffer::NzVertexBuffer(const NzVertexDeclaration* vertexDeclaration, NzBuffer* buffer, unsigned int startOffset, unsigned int endOffset)
|
||||
{
|
||||
NzErrorFlags(nzErrorFlag_ThrowException, true);
|
||||
Reset(vertexDeclaration, buffer, startOffset, endOffset);
|
||||
}
|
||||
|
||||
NzVertexBuffer::NzVertexBuffer(const NzVertexDeclaration* vertexDeclaration, unsigned int length, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
NzErrorFlags(nzErrorFlag_ThrowException, true);
|
||||
Reset(vertexDeclaration, length, storage, usage);
|
||||
}
|
||||
|
||||
NzVertexBuffer::NzVertexBuffer(const NzVertexBuffer& vertexBuffer) :
|
||||
NzRefCounted(),
|
||||
m_buffer(vertexBuffer.m_buffer),
|
||||
m_vertexDeclaration(vertexBuffer.m_vertexDeclaration),
|
||||
m_endOffset(vertexBuffer.m_endOffset),
|
||||
m_startOffset(vertexBuffer.m_startOffset),
|
||||
m_vertexCount(vertexBuffer.m_vertexCount)
|
||||
{
|
||||
}
|
||||
|
||||
NzVertexBuffer::~NzVertexBuffer()
|
||||
{
|
||||
OnVertexBufferRelease(this);
|
||||
}
|
||||
|
||||
bool NzVertexBuffer::Fill(const void* data, unsigned int startVertex, unsigned int length, bool forceDiscard)
|
||||
{
|
||||
unsigned int stride = m_vertexDeclaration->GetStride();
|
||||
return FillRaw(data, startVertex*stride, length*stride, forceDiscard);
|
||||
}
|
||||
|
||||
bool NzVertexBuffer::FillRaw(const void* data, unsigned int offset, unsigned int size, bool forceDiscard)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_buffer)
|
||||
VertexBuffer::VertexBuffer(const VertexDeclaration* vertexDeclaration, Buffer* buffer)
|
||||
{
|
||||
NazaraError("No buffer");
|
||||
return false;
|
||||
ErrorFlags(ErrorFlag_ThrowException, true);
|
||||
Reset(vertexDeclaration, buffer);
|
||||
}
|
||||
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
VertexBuffer::VertexBuffer(const VertexDeclaration* vertexDeclaration, Buffer* buffer, unsigned int startOffset, unsigned int endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Fill(data, m_startOffset+offset, size, forceDiscard);
|
||||
}
|
||||
|
||||
NzBuffer* NzVertexBuffer::GetBuffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
unsigned int NzVertexBuffer::GetEndOffset() const
|
||||
{
|
||||
return m_endOffset;
|
||||
}
|
||||
|
||||
unsigned int NzVertexBuffer::GetStartOffset() const
|
||||
{
|
||||
return m_startOffset;
|
||||
}
|
||||
|
||||
unsigned int NzVertexBuffer::GetStride() const
|
||||
{
|
||||
return m_vertexDeclaration->GetStride();
|
||||
}
|
||||
|
||||
unsigned int NzVertexBuffer::GetVertexCount() const
|
||||
{
|
||||
return m_vertexCount;
|
||||
}
|
||||
|
||||
const NzVertexDeclaration* NzVertexBuffer::GetVertexDeclaration() const
|
||||
{
|
||||
return m_vertexDeclaration;
|
||||
}
|
||||
|
||||
bool NzVertexBuffer::IsHardware() const
|
||||
{
|
||||
return m_buffer->IsHardware();
|
||||
}
|
||||
|
||||
bool NzVertexBuffer::IsValid() const
|
||||
{
|
||||
return m_buffer && m_vertexDeclaration;
|
||||
}
|
||||
|
||||
void* NzVertexBuffer::Map(nzBufferAccess access, unsigned int startVertex, unsigned int length)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_vertexDeclaration)
|
||||
{
|
||||
NazaraError("No vertex declaration");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int stride = m_vertexDeclaration->GetStride();
|
||||
|
||||
return MapRaw(access, startVertex*stride, length*stride);
|
||||
}
|
||||
|
||||
void* NzVertexBuffer::Map(nzBufferAccess access, unsigned int startVertex, unsigned int length) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_buffer)
|
||||
{
|
||||
NazaraError("No buffer");
|
||||
return nullptr;
|
||||
ErrorFlags(ErrorFlag_ThrowException, true);
|
||||
Reset(vertexDeclaration, buffer, startOffset, endOffset);
|
||||
}
|
||||
|
||||
if (!m_vertexDeclaration)
|
||||
VertexBuffer::VertexBuffer(const VertexDeclaration* vertexDeclaration, unsigned int length, UInt32 storage, BufferUsage usage)
|
||||
{
|
||||
NazaraError("No vertex declaration");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int stride = m_vertexDeclaration->GetStride();
|
||||
|
||||
return MapRaw(access, startVertex*stride, length*stride);
|
||||
}
|
||||
|
||||
void* NzVertexBuffer::MapRaw(nzBufferAccess access, unsigned int offset, unsigned int size)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_buffer)
|
||||
{
|
||||
NazaraError("No buffer");
|
||||
return nullptr;
|
||||
ErrorFlags(ErrorFlag_ThrowException, true);
|
||||
Reset(vertexDeclaration, length, storage, usage);
|
||||
}
|
||||
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
VertexBuffer::VertexBuffer(const VertexBuffer& vertexBuffer) :
|
||||
RefCounted(),
|
||||
m_buffer(vertexBuffer.m_buffer),
|
||||
m_vertexDeclaration(vertexBuffer.m_vertexDeclaration),
|
||||
m_endOffset(vertexBuffer.m_endOffset),
|
||||
m_startOffset(vertexBuffer.m_startOffset),
|
||||
m_vertexCount(vertexBuffer.m_vertexCount)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Map(access, offset, size);
|
||||
}
|
||||
|
||||
void* NzVertexBuffer::MapRaw(nzBufferAccess access, unsigned int offset, unsigned int size) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Map(access, offset, size);
|
||||
}
|
||||
|
||||
void NzVertexBuffer::Reset()
|
||||
{
|
||||
m_buffer.Reset();
|
||||
m_vertexDeclaration.Reset();
|
||||
}
|
||||
|
||||
void NzVertexBuffer::Reset(const NzVertexDeclaration* vertexDeclaration, NzBuffer* buffer)
|
||||
{
|
||||
Reset(vertexDeclaration, buffer, 0, buffer->GetSize()-1);
|
||||
}
|
||||
|
||||
void NzVertexBuffer::Reset(const NzVertexDeclaration* vertexDeclaration, NzBuffer* buffer, unsigned int startOffset, unsigned int endOffset)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!buffer || !buffer->IsValid())
|
||||
{
|
||||
NazaraError("Invalid buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (startOffset > endOffset)
|
||||
VertexBuffer::~VertexBuffer()
|
||||
{
|
||||
NazaraError("Start offset cannot be over end offset");
|
||||
return;
|
||||
OnVertexBufferRelease(this);
|
||||
}
|
||||
|
||||
unsigned int bufferSize = buffer->GetSize();
|
||||
if (startOffset >= bufferSize)
|
||||
bool VertexBuffer::Fill(const void* data, unsigned int startVertex, unsigned int length, bool forceDiscard)
|
||||
{
|
||||
NazaraError("Start offset is over buffer size");
|
||||
return;
|
||||
unsigned int stride = m_vertexDeclaration->GetStride();
|
||||
return FillRaw(data, startVertex*stride, length*stride, forceDiscard);
|
||||
}
|
||||
|
||||
if (endOffset >= bufferSize)
|
||||
bool VertexBuffer::FillRaw(const void* data, unsigned int offset, unsigned int size, bool forceDiscard)
|
||||
{
|
||||
NazaraError("End offset is over buffer size");
|
||||
return;
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_buffer)
|
||||
{
|
||||
NazaraError("No buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Fill(data, m_startOffset+offset, size, forceDiscard);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_buffer = buffer;
|
||||
m_endOffset = endOffset;
|
||||
m_startOffset = startOffset;
|
||||
m_vertexCount = (vertexDeclaration) ? ((endOffset - startOffset) / vertexDeclaration->GetStride()) : 0;
|
||||
m_vertexDeclaration = vertexDeclaration;
|
||||
}
|
||||
|
||||
void NzVertexBuffer::Reset(const NzVertexDeclaration* vertexDeclaration, unsigned int length, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
m_endOffset = length * ((vertexDeclaration) ? vertexDeclaration->GetStride() : 1);
|
||||
m_startOffset = 0;
|
||||
m_vertexCount = length;
|
||||
|
||||
m_buffer = NzBuffer::New(nzBufferType_Vertex, m_endOffset, storage, usage);
|
||||
m_vertexDeclaration = vertexDeclaration;
|
||||
}
|
||||
|
||||
void NzVertexBuffer::Reset(const NzVertexBuffer& vertexBuffer)
|
||||
{
|
||||
m_buffer = vertexBuffer.m_buffer;
|
||||
m_endOffset = vertexBuffer.m_endOffset;
|
||||
m_startOffset = vertexBuffer.m_startOffset;
|
||||
m_vertexCount = vertexBuffer.m_vertexCount;
|
||||
m_vertexDeclaration = vertexBuffer.m_vertexDeclaration;
|
||||
}
|
||||
|
||||
bool NzVertexBuffer::SetStorage(nzUInt32 storage)
|
||||
{
|
||||
return m_buffer->SetStorage(storage);
|
||||
}
|
||||
|
||||
void NzVertexBuffer::SetVertexDeclaration(const NzVertexDeclaration* vertexDeclaration)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!vertexDeclaration)
|
||||
Buffer* VertexBuffer::GetBuffer() const
|
||||
{
|
||||
NazaraError("Vertex declaration is invalid");
|
||||
return;
|
||||
return m_buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_vertexCount = (m_endOffset - m_startOffset)/vertexDeclaration->GetStride();
|
||||
m_vertexDeclaration = vertexDeclaration;
|
||||
}
|
||||
|
||||
void NzVertexBuffer::Unmap() const
|
||||
{
|
||||
m_buffer->Unmap();
|
||||
}
|
||||
|
||||
NzVertexBuffer& NzVertexBuffer::operator=(const NzVertexBuffer& vertexBuffer)
|
||||
{
|
||||
Reset(vertexBuffer);
|
||||
|
||||
return *this;
|
||||
unsigned int VertexBuffer::GetEndOffset() const
|
||||
{
|
||||
return m_endOffset;
|
||||
}
|
||||
|
||||
unsigned int VertexBuffer::GetStartOffset() const
|
||||
{
|
||||
return m_startOffset;
|
||||
}
|
||||
|
||||
unsigned int VertexBuffer::GetStride() const
|
||||
{
|
||||
return m_vertexDeclaration->GetStride();
|
||||
}
|
||||
|
||||
unsigned int VertexBuffer::GetVertexCount() const
|
||||
{
|
||||
return m_vertexCount;
|
||||
}
|
||||
|
||||
const VertexDeclaration* VertexBuffer::GetVertexDeclaration() const
|
||||
{
|
||||
return m_vertexDeclaration;
|
||||
}
|
||||
|
||||
bool VertexBuffer::IsHardware() const
|
||||
{
|
||||
return m_buffer->IsHardware();
|
||||
}
|
||||
|
||||
bool VertexBuffer::IsValid() const
|
||||
{
|
||||
return m_buffer && m_vertexDeclaration;
|
||||
}
|
||||
|
||||
void* VertexBuffer::Map(BufferAccess access, unsigned int startVertex, unsigned int length)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_vertexDeclaration)
|
||||
{
|
||||
NazaraError("No vertex declaration");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int stride = m_vertexDeclaration->GetStride();
|
||||
|
||||
return MapRaw(access, startVertex*stride, length*stride);
|
||||
}
|
||||
|
||||
void* VertexBuffer::Map(BufferAccess access, unsigned int startVertex, unsigned int length) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_buffer)
|
||||
{
|
||||
NazaraError("No buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!m_vertexDeclaration)
|
||||
{
|
||||
NazaraError("No vertex declaration");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int stride = m_vertexDeclaration->GetStride();
|
||||
|
||||
return MapRaw(access, startVertex*stride, length*stride);
|
||||
}
|
||||
|
||||
void* VertexBuffer::MapRaw(BufferAccess access, unsigned int offset, unsigned int size)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_buffer)
|
||||
{
|
||||
NazaraError("No buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Map(access, offset, size);
|
||||
}
|
||||
|
||||
void* VertexBuffer::MapRaw(BufferAccess access, unsigned int offset, unsigned int size) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_buffer->Map(access, offset, size);
|
||||
}
|
||||
|
||||
void VertexBuffer::Reset()
|
||||
{
|
||||
m_buffer.Reset();
|
||||
m_vertexDeclaration.Reset();
|
||||
}
|
||||
|
||||
void VertexBuffer::Reset(const VertexDeclaration* vertexDeclaration, Buffer* buffer)
|
||||
{
|
||||
Reset(vertexDeclaration, buffer, 0, buffer->GetSize()-1);
|
||||
}
|
||||
|
||||
void VertexBuffer::Reset(const VertexDeclaration* vertexDeclaration, Buffer* buffer, unsigned int startOffset, unsigned int endOffset)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!buffer || !buffer->IsValid())
|
||||
{
|
||||
NazaraError("Invalid buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (startOffset > endOffset)
|
||||
{
|
||||
NazaraError("Start offset cannot be over end offset");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int bufferSize = buffer->GetSize();
|
||||
if (startOffset >= bufferSize)
|
||||
{
|
||||
NazaraError("Start offset is over buffer size");
|
||||
return;
|
||||
}
|
||||
|
||||
if (endOffset >= bufferSize)
|
||||
{
|
||||
NazaraError("End offset is over buffer size");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_buffer = buffer;
|
||||
m_endOffset = endOffset;
|
||||
m_startOffset = startOffset;
|
||||
m_vertexCount = (vertexDeclaration) ? ((endOffset - startOffset) / vertexDeclaration->GetStride()) : 0;
|
||||
m_vertexDeclaration = vertexDeclaration;
|
||||
}
|
||||
|
||||
void VertexBuffer::Reset(const VertexDeclaration* vertexDeclaration, unsigned int length, UInt32 storage, BufferUsage usage)
|
||||
{
|
||||
m_endOffset = length * ((vertexDeclaration) ? vertexDeclaration->GetStride() : 1);
|
||||
m_startOffset = 0;
|
||||
m_vertexCount = length;
|
||||
|
||||
m_buffer = Buffer::New(BufferType_Vertex, m_endOffset, storage, usage);
|
||||
m_vertexDeclaration = vertexDeclaration;
|
||||
}
|
||||
|
||||
void VertexBuffer::Reset(const VertexBuffer& vertexBuffer)
|
||||
{
|
||||
m_buffer = vertexBuffer.m_buffer;
|
||||
m_endOffset = vertexBuffer.m_endOffset;
|
||||
m_startOffset = vertexBuffer.m_startOffset;
|
||||
m_vertexCount = vertexBuffer.m_vertexCount;
|
||||
m_vertexDeclaration = vertexBuffer.m_vertexDeclaration;
|
||||
}
|
||||
|
||||
bool VertexBuffer::SetStorage(UInt32 storage)
|
||||
{
|
||||
return m_buffer->SetStorage(storage);
|
||||
}
|
||||
|
||||
void VertexBuffer::SetVertexDeclaration(const VertexDeclaration* vertexDeclaration)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!vertexDeclaration)
|
||||
{
|
||||
NazaraError("Vertex declaration is invalid");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_vertexCount = (m_endOffset - m_startOffset)/vertexDeclaration->GetStride();
|
||||
m_vertexDeclaration = vertexDeclaration;
|
||||
}
|
||||
|
||||
void VertexBuffer::Unmap() const
|
||||
{
|
||||
m_buffer->Unmap();
|
||||
}
|
||||
|
||||
VertexBuffer& VertexBuffer::operator=(const VertexBuffer& vertexBuffer)
|
||||
{
|
||||
Reset(vertexBuffer);
|
||||
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,291 +13,294 @@
|
||||
#include <cstring>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzVertexDeclaration::NzVertexDeclaration() :
|
||||
m_stride(0)
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
NzVertexDeclaration::NzVertexDeclaration(const NzVertexDeclaration& declaration) :
|
||||
NzRefCounted(),
|
||||
m_stride(declaration.m_stride)
|
||||
{
|
||||
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(nzVertexComponent_Max+1));
|
||||
}
|
||||
|
||||
NzVertexDeclaration::~NzVertexDeclaration()
|
||||
{
|
||||
OnVertexDeclarationRelease(this);
|
||||
}
|
||||
|
||||
void NzVertexDeclaration::DisableComponent(nzVertexComponent component)
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (component > nzVertexComponent_Max)
|
||||
VertexDeclaration::VertexDeclaration() :
|
||||
m_stride(0)
|
||||
{
|
||||
NazaraError("Vertex component out of enum");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (component == nzVertexComponent_Unused)
|
||||
VertexDeclaration::VertexDeclaration(const VertexDeclaration& declaration) :
|
||||
RefCounted(),
|
||||
m_stride(declaration.m_stride)
|
||||
{
|
||||
NazaraError("Cannot disable \"unused\" component");
|
||||
return;
|
||||
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(VertexComponent_Max+1));
|
||||
}
|
||||
#endif
|
||||
|
||||
Component& vertexComponent = m_components[component];
|
||||
if (vertexComponent.enabled)
|
||||
VertexDeclaration::~VertexDeclaration()
|
||||
{
|
||||
vertexComponent.enabled = false;
|
||||
m_stride -= NzUtility::ComponentStride[vertexComponent.type];
|
||||
OnVertexDeclarationRelease(this);
|
||||
}
|
||||
}
|
||||
|
||||
void NzVertexDeclaration::EnableComponent(nzVertexComponent component, nzComponentType type, unsigned int offset)
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (component > nzVertexComponent_Max)
|
||||
void VertexDeclaration::DisableComponent(VertexComponent component)
|
||||
{
|
||||
NazaraError("Vertex component out of enum");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (component > VertexComponent_Max)
|
||||
{
|
||||
NazaraError("Vertex component out of enum");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!IsTypeSupported(type))
|
||||
{
|
||||
NazaraError("Component type 0x" + NzString::Number(type, 16) + " is not supported by vertex declarations");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (component == VertexComponent_Unused)
|
||||
{
|
||||
NazaraError("Cannot disable \"unused\" component");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (component != nzVertexComponent_Unused)
|
||||
{
|
||||
Component& vertexComponent = m_components[component];
|
||||
if (vertexComponent.enabled)
|
||||
m_stride -= NzUtility::ComponentStride[vertexComponent.type];
|
||||
else
|
||||
vertexComponent.enabled = true;
|
||||
|
||||
vertexComponent.offset = offset;
|
||||
vertexComponent.type = type;
|
||||
{
|
||||
vertexComponent.enabled = false;
|
||||
m_stride -= Utility::ComponentStride[vertexComponent.type];
|
||||
}
|
||||
}
|
||||
|
||||
m_stride += NzUtility::ComponentStride[type];
|
||||
}
|
||||
|
||||
void NzVertexDeclaration::GetComponent(nzVertexComponent component, bool* enabled, nzComponentType* type, unsigned int* offset) const
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (component > nzVertexComponent_Max)
|
||||
void VertexDeclaration::EnableComponent(VertexComponent component, ComponentType type, unsigned int offset)
|
||||
{
|
||||
NazaraError("Vertex component out of enum");
|
||||
return;
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (component > VertexComponent_Max)
|
||||
{
|
||||
NazaraError("Vertex component out of enum");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!IsTypeSupported(type))
|
||||
{
|
||||
NazaraError("Component type 0x" + String::Number(type, 16) + " is not supported by vertex declarations");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (component != VertexComponent_Unused)
|
||||
{
|
||||
Component& vertexComponent = m_components[component];
|
||||
if (vertexComponent.enabled)
|
||||
m_stride -= Utility::ComponentStride[vertexComponent.type];
|
||||
else
|
||||
vertexComponent.enabled = true;
|
||||
|
||||
vertexComponent.offset = offset;
|
||||
vertexComponent.type = type;
|
||||
}
|
||||
|
||||
m_stride += Utility::ComponentStride[type];
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (component == nzVertexComponent_Unused)
|
||||
void VertexDeclaration::GetComponent(VertexComponent component, bool* enabled, ComponentType* type, unsigned int* offset) const
|
||||
{
|
||||
NazaraError("Cannot get \"unused\" component");
|
||||
return;
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (component > VertexComponent_Max)
|
||||
{
|
||||
NazaraError("Vertex component out of enum");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (component == VertexComponent_Unused)
|
||||
{
|
||||
NazaraError("Cannot get \"unused\" component");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
const Component& vertexComponent = m_components[component];
|
||||
|
||||
if (enabled)
|
||||
*enabled = vertexComponent.enabled;
|
||||
|
||||
if (type)
|
||||
*type = vertexComponent.type;
|
||||
|
||||
if (offset)
|
||||
*offset = vertexComponent.offset;
|
||||
}
|
||||
#endif
|
||||
|
||||
const Component& vertexComponent = m_components[component];
|
||||
|
||||
if (enabled)
|
||||
*enabled = vertexComponent.enabled;
|
||||
|
||||
if (type)
|
||||
*type = vertexComponent.type;
|
||||
|
||||
if (offset)
|
||||
*offset = vertexComponent.offset;
|
||||
}
|
||||
|
||||
unsigned int NzVertexDeclaration::GetStride() const
|
||||
{
|
||||
return m_stride;
|
||||
}
|
||||
|
||||
void NzVertexDeclaration::SetStride(unsigned int stride)
|
||||
{
|
||||
m_stride = stride;
|
||||
}
|
||||
|
||||
NzVertexDeclaration& NzVertexDeclaration::operator=(const NzVertexDeclaration& declaration)
|
||||
{
|
||||
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(nzVertexComponent_Max+1));
|
||||
m_stride = declaration.m_stride;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NzVertexDeclaration* NzVertexDeclaration::Get(nzVertexLayout layout)
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (layout > nzVertexLayout_Max)
|
||||
unsigned int VertexDeclaration::GetStride() const
|
||||
{
|
||||
NazaraError("Vertex layout out of enum");
|
||||
return nullptr;
|
||||
return m_stride;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &s_declarations[layout];
|
||||
}
|
||||
|
||||
bool NzVertexDeclaration::IsTypeSupported(nzComponentType type)
|
||||
{
|
||||
switch (type)
|
||||
void VertexDeclaration::SetStride(unsigned int stride)
|
||||
{
|
||||
case nzComponentType_Color:
|
||||
case nzComponentType_Double1:
|
||||
case nzComponentType_Double2:
|
||||
case nzComponentType_Double3:
|
||||
case nzComponentType_Double4:
|
||||
case nzComponentType_Float1:
|
||||
case nzComponentType_Float2:
|
||||
case nzComponentType_Float3:
|
||||
case nzComponentType_Float4:
|
||||
case nzComponentType_Int1:
|
||||
case nzComponentType_Int2:
|
||||
case nzComponentType_Int3:
|
||||
case nzComponentType_Int4:
|
||||
return true;
|
||||
m_stride = stride;
|
||||
}
|
||||
|
||||
case nzComponentType_Quaternion:
|
||||
VertexDeclaration& VertexDeclaration::operator=(const VertexDeclaration& declaration)
|
||||
{
|
||||
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(VertexComponent_Max+1));
|
||||
m_stride = declaration.m_stride;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
VertexDeclaration* VertexDeclaration::Get(VertexLayout layout)
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (layout > VertexLayout_Max)
|
||||
{
|
||||
NazaraError("Vertex layout out of enum");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return &s_declarations[layout];
|
||||
}
|
||||
|
||||
bool VertexDeclaration::IsTypeSupported(ComponentType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ComponentType_Color:
|
||||
case ComponentType_Double1:
|
||||
case ComponentType_Double2:
|
||||
case ComponentType_Double3:
|
||||
case ComponentType_Double4:
|
||||
case ComponentType_Float1:
|
||||
case ComponentType_Float2:
|
||||
case ComponentType_Float3:
|
||||
case ComponentType_Float4:
|
||||
case ComponentType_Int1:
|
||||
case ComponentType_Int2:
|
||||
case ComponentType_Int3:
|
||||
case ComponentType_Int4:
|
||||
return true;
|
||||
|
||||
case ComponentType_Quaternion:
|
||||
return false;
|
||||
}
|
||||
|
||||
NazaraError("Component type not handled (0x" + String::Number(type, 16) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VertexDeclaration::Initialize()
|
||||
{
|
||||
if (!VertexDeclarationLibrary::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialise library");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ErrorFlags flags(ErrorFlag_Silent | ErrorFlag_ThrowException);
|
||||
|
||||
// Layout : Type
|
||||
VertexDeclaration* declaration;
|
||||
|
||||
// VertexLayout_XY : VertexStruct_XY
|
||||
declaration = &s_declarations[VertexLayout_XY];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float2, NazaraOffsetOf(VertexStruct_XY, position));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XY), "Invalid stride for declaration VertexLayout_XY");
|
||||
|
||||
// VertexLayout_XY_Color : VertexStruct_XY_Color
|
||||
declaration = &s_declarations[VertexLayout_XY_Color];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float2, NazaraOffsetOf(VertexStruct_XY_Color, position));
|
||||
declaration->EnableComponent(VertexComponent_Color, ComponentType_Color, NazaraOffsetOf(VertexStruct_XY_Color, color));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XY_Color), "Invalid stride for declaration VertexLayout_XY_Color");
|
||||
|
||||
// VertexLayout_XY_UV : VertexStruct_XY_UV
|
||||
declaration = &s_declarations[VertexLayout_XY_UV];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float2, NazaraOffsetOf(VertexStruct_XY_UV, position));
|
||||
declaration->EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(VertexStruct_XY_UV, uv));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XY_UV), "Invalid stride for declaration VertexLayout_XY_UV");
|
||||
|
||||
// VertexLayout_XYZ : VertexStruct_XYZ
|
||||
declaration = &s_declarations[VertexLayout_XYZ];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ, position));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XYZ), "Invalid stride for declaration VertexLayout_XYZ");
|
||||
|
||||
// VertexLayout_XYZ_Color : VertexStruct_XYZ_Color
|
||||
declaration = &s_declarations[VertexLayout_XYZ_Color];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Color, position));
|
||||
declaration->EnableComponent(VertexComponent_Color, ComponentType_Color, NazaraOffsetOf(VertexStruct_XYZ_Color, color));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XYZ_Color), "Invalid stride for declaration VertexLayout_XYZ_Color");
|
||||
|
||||
// VertexLayout_XYZ_Color_UV : VertexStruct_XYZ_Color_UV
|
||||
declaration = &s_declarations[VertexLayout_XYZ_Color_UV];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Color_UV, position));
|
||||
declaration->EnableComponent(VertexComponent_Color, ComponentType_Color, NazaraOffsetOf(VertexStruct_XYZ_Color_UV, color));
|
||||
declaration->EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(VertexStruct_XYZ_Color_UV, uv));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XYZ_Color_UV), "Invalid stride for declaration VertexLayout_XYZ_Color_UV");
|
||||
|
||||
// VertexLayout_XYZ_Normal : VertexStruct_XYZ_Normal
|
||||
declaration = &s_declarations[VertexLayout_XYZ_Normal];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal, position));
|
||||
declaration->EnableComponent(VertexComponent_Normal, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal, normal));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XYZ_Normal), "Invalid stride for declaration VertexLayout_XYZ_Normal");
|
||||
|
||||
// VertexLayout_XYZ_Normal_UV : VertexStruct_XYZ_Normal_UV
|
||||
declaration = &s_declarations[VertexLayout_XYZ_Normal_UV];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV, position));
|
||||
declaration->EnableComponent(VertexComponent_Normal, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV, normal));
|
||||
declaration->EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV, uv));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XYZ_Normal_UV), "Invalid stride for declaration VertexLayout_XYZ_Normal_UV");
|
||||
|
||||
// VertexLayout_XYZ_Normal_UV_Tangent : VertexStruct_XYZ_Normal_UV_Tangent
|
||||
declaration = &s_declarations[VertexLayout_XYZ_Normal_UV_Tangent];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent, position));
|
||||
declaration->EnableComponent(VertexComponent_Normal, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent, normal));
|
||||
declaration->EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent, uv));
|
||||
declaration->EnableComponent(VertexComponent_Tangent, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent, tangent));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XYZ_Normal_UV_Tangent), "Invalid stride for declaration VertexLayout_XYZ_Normal_UV_Tangent");
|
||||
|
||||
// VertexLayout_XYZ_Normal_UV_Tangent_Skinning : VertexStruct_XYZ_Normal_UV_Tangent_Skinning
|
||||
declaration = &s_declarations[VertexLayout_XYZ_Normal_UV_Tangent_Skinning];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent_Skinning, position));
|
||||
declaration->EnableComponent(VertexComponent_Normal, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent_Skinning, normal));
|
||||
declaration->EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent_Skinning, uv));
|
||||
declaration->EnableComponent(VertexComponent_Tangent, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent_Skinning, tangent));
|
||||
declaration->EnableComponent(VertexComponent_Unused, ComponentType_Int1, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent_Skinning, weightCount));
|
||||
declaration->EnableComponent(VertexComponent_Userdata0, ComponentType_Float4, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent_Skinning, weights));
|
||||
declaration->EnableComponent(VertexComponent_Userdata1, ComponentType_Int4, NazaraOffsetOf(VertexStruct_XYZ_Normal_UV_Tangent_Skinning, jointIndexes));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XYZ_Normal_UV_Tangent_Skinning), "Invalid stride for declaration VertexLayout_XYZ_Normal_UV_Tangent_Skinning");
|
||||
|
||||
// VertexLayout_XYZ_UV : VertexStruct_XYZ_UV
|
||||
declaration = &s_declarations[VertexLayout_XYZ_UV];
|
||||
declaration->EnableComponent(VertexComponent_Position, ComponentType_Float3, NazaraOffsetOf(VertexStruct_XYZ_UV, position));
|
||||
declaration->EnableComponent(VertexComponent_TexCoord, ComponentType_Float2, NazaraOffsetOf(VertexStruct_XYZ_UV, uv));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(VertexStruct_XYZ_UV), "Invalid stride for declaration VertexLayout_XYZ_UV");
|
||||
|
||||
// VertexLayout_Matrix4 : Matrix4f
|
||||
declaration = &s_declarations[VertexLayout_Matrix4];
|
||||
declaration->EnableComponent(VertexComponent_InstanceData0, ComponentType_Float4, NazaraOffsetOf(Matrix4f, m11));
|
||||
declaration->EnableComponent(VertexComponent_InstanceData1, ComponentType_Float4, NazaraOffsetOf(Matrix4f, m21));
|
||||
declaration->EnableComponent(VertexComponent_InstanceData2, ComponentType_Float4, NazaraOffsetOf(Matrix4f, m31));
|
||||
declaration->EnableComponent(VertexComponent_InstanceData3, ComponentType_Float4, NazaraOffsetOf(Matrix4f, m41));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(Matrix4f), "Invalid stride for declaration VertexLayout_Matrix4");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
NazaraError("Failed to initialize vertex declaration: " + String(e.what()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NazaraError("Component type not handled (0x" + NzString::Number(type, 16) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NzVertexDeclaration::Initialize()
|
||||
{
|
||||
if (!NzVertexDeclarationLibrary::Initialize())
|
||||
void VertexDeclaration::Uninitialize()
|
||||
{
|
||||
NazaraError("Failed to initialise library");
|
||||
return false;
|
||||
VertexDeclarationLibrary::Uninitialize();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_Silent | nzErrorFlag_ThrowException);
|
||||
|
||||
// Layout : Type
|
||||
NzVertexDeclaration* declaration;
|
||||
|
||||
// nzVertexLayout_XY : NzVertexStruct_XY
|
||||
declaration = &s_declarations[nzVertexLayout_XY];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float2, NzOffsetOf(NzVertexStruct_XY, position));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XY), "Invalid stride for declaration nzVertexLayout_XY");
|
||||
|
||||
// nzVertexLayout_XY_Color : NzVertexStruct_XY_Color
|
||||
declaration = &s_declarations[nzVertexLayout_XY_Color];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float2, NzOffsetOf(NzVertexStruct_XY_Color, position));
|
||||
declaration->EnableComponent(nzVertexComponent_Color, nzComponentType_Color, NzOffsetOf(NzVertexStruct_XY_Color, color));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XY_Color), "Invalid stride for declaration nzVertexLayout_XY_Color");
|
||||
|
||||
// nzVertexLayout_XY_UV : NzVertexStruct_XY_UV
|
||||
declaration = &s_declarations[nzVertexLayout_XY_UV];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float2, NzOffsetOf(NzVertexStruct_XY_UV, position));
|
||||
declaration->EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(NzVertexStruct_XY_UV, uv));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XY_UV), "Invalid stride for declaration nzVertexLayout_XY_UV");
|
||||
|
||||
// nzVertexLayout_XYZ : NzVertexStruct_XYZ
|
||||
declaration = &s_declarations[nzVertexLayout_XYZ];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ, position));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XYZ), "Invalid stride for declaration nzVertexLayout_XYZ");
|
||||
|
||||
// nzVertexLayout_XYZ_Color : NzVertexStruct_XYZ_Color
|
||||
declaration = &s_declarations[nzVertexLayout_XYZ_Color];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Color, position));
|
||||
declaration->EnableComponent(nzVertexComponent_Color, nzComponentType_Color, NzOffsetOf(NzVertexStruct_XYZ_Color, color));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XYZ_Color), "Invalid stride for declaration nzVertexLayout_XYZ_Color");
|
||||
|
||||
// nzVertexLayout_XYZ_Color_UV : NzVertexStruct_XYZ_Color_UV
|
||||
declaration = &s_declarations[nzVertexLayout_XYZ_Color_UV];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Color_UV, position));
|
||||
declaration->EnableComponent(nzVertexComponent_Color, nzComponentType_Color, NzOffsetOf(NzVertexStruct_XYZ_Color_UV, color));
|
||||
declaration->EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(NzVertexStruct_XYZ_Color_UV, uv));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XYZ_Color_UV), "Invalid stride for declaration nzVertexLayout_XYZ_Color_UV");
|
||||
|
||||
// nzVertexLayout_XYZ_Normal : NzVertexStruct_XYZ_Normal
|
||||
declaration = &s_declarations[nzVertexLayout_XYZ_Normal];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal, position));
|
||||
declaration->EnableComponent(nzVertexComponent_Normal, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal, normal));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XYZ_Normal), "Invalid stride for declaration nzVertexLayout_XYZ_Normal");
|
||||
|
||||
// nzVertexLayout_XYZ_Normal_UV : NzVertexStruct_XYZ_Normal_UV
|
||||
declaration = &s_declarations[nzVertexLayout_XYZ_Normal_UV];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV, position));
|
||||
declaration->EnableComponent(nzVertexComponent_Normal, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV, normal));
|
||||
declaration->EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV, uv));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XYZ_Normal_UV), "Invalid stride for declaration nzVertexLayout_XYZ_Normal_UV");
|
||||
|
||||
// nzVertexLayout_XYZ_Normal_UV_Tangent : NzVertexStruct_XYZ_Normal_UV_Tangent
|
||||
declaration = &s_declarations[nzVertexLayout_XYZ_Normal_UV_Tangent];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent, position));
|
||||
declaration->EnableComponent(nzVertexComponent_Normal, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent, normal));
|
||||
declaration->EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent, uv));
|
||||
declaration->EnableComponent(nzVertexComponent_Tangent, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent, tangent));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XYZ_Normal_UV_Tangent), "Invalid stride for declaration nzVertexLayout_XYZ_Normal_UV_Tangent");
|
||||
|
||||
// nzVertexLayout_XYZ_Normal_UV_Tangent_Skinning : NzVertexStruct_XYZ_Normal_UV_Tangent_Skinning
|
||||
declaration = &s_declarations[nzVertexLayout_XYZ_Normal_UV_Tangent_Skinning];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent_Skinning, position));
|
||||
declaration->EnableComponent(nzVertexComponent_Normal, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent_Skinning, normal));
|
||||
declaration->EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent_Skinning, uv));
|
||||
declaration->EnableComponent(nzVertexComponent_Tangent, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent_Skinning, tangent));
|
||||
declaration->EnableComponent(nzVertexComponent_Unused, nzComponentType_Int1, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent_Skinning, weightCount));
|
||||
declaration->EnableComponent(nzVertexComponent_Userdata0, nzComponentType_Float4, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent_Skinning, weights));
|
||||
declaration->EnableComponent(nzVertexComponent_Userdata1, nzComponentType_Int4, NzOffsetOf(NzVertexStruct_XYZ_Normal_UV_Tangent_Skinning, jointIndexes));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XYZ_Normal_UV_Tangent_Skinning), "Invalid stride for declaration nzVertexLayout_XYZ_Normal_UV_Tangent_Skinning");
|
||||
|
||||
// nzVertexLayout_XYZ_UV : NzVertexStruct_XYZ_UV
|
||||
declaration = &s_declarations[nzVertexLayout_XYZ_UV];
|
||||
declaration->EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(NzVertexStruct_XYZ_UV, position));
|
||||
declaration->EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(NzVertexStruct_XYZ_UV, uv));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzVertexStruct_XYZ_UV), "Invalid stride for declaration nzVertexLayout_XYZ_UV");
|
||||
|
||||
// nzVertexLayout_Matrix4 : NzMatrix4f
|
||||
declaration = &s_declarations[nzVertexLayout_Matrix4];
|
||||
declaration->EnableComponent(nzVertexComponent_InstanceData0, nzComponentType_Float4, NzOffsetOf(NzMatrix4f, m11));
|
||||
declaration->EnableComponent(nzVertexComponent_InstanceData1, nzComponentType_Float4, NzOffsetOf(NzMatrix4f, m21));
|
||||
declaration->EnableComponent(nzVertexComponent_InstanceData2, nzComponentType_Float4, NzOffsetOf(NzMatrix4f, m31));
|
||||
declaration->EnableComponent(nzVertexComponent_InstanceData3, nzComponentType_Float4, NzOffsetOf(NzMatrix4f, m41));
|
||||
|
||||
NazaraAssert(declaration->GetStride() == sizeof(NzMatrix4f), "Invalid stride for declaration nzVertexLayout_Matrix4");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
NazaraError("Failed to initialize vertex declaration: " + NzString(e.what()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
VertexDeclaration VertexDeclaration::s_declarations[VertexLayout_Max+1];
|
||||
VertexDeclarationLibrary::LibraryMap VertexDeclaration::s_library;
|
||||
}
|
||||
|
||||
void NzVertexDeclaration::Uninitialize()
|
||||
{
|
||||
NzVertexDeclarationLibrary::Uninitialize();
|
||||
}
|
||||
|
||||
NzVertexDeclaration NzVertexDeclaration::s_declarations[nzVertexLayout_Max+1];
|
||||
NzVertexDeclarationLibrary::LibraryMap NzVertexDeclaration::s_library;
|
||||
|
||||
@@ -10,45 +10,48 @@
|
||||
#include <Nazara/Utility/SubMesh.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzVertexMapper::NzVertexMapper(NzSubMesh* subMesh, nzBufferAccess access)
|
||||
namespace Nz
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
|
||||
|
||||
NzVertexBuffer* buffer = nullptr;
|
||||
switch (subMesh->GetAnimationType())
|
||||
VertexMapper::VertexMapper(SubMesh* subMesh, BufferAccess access)
|
||||
{
|
||||
case nzAnimationType_Skeletal:
|
||||
ErrorFlags flags(ErrorFlag_ThrowException, true);
|
||||
|
||||
VertexBuffer* buffer = nullptr;
|
||||
switch (subMesh->GetAnimationType())
|
||||
{
|
||||
NzSkeletalMesh* skeletalMesh = static_cast<NzSkeletalMesh*>(subMesh);
|
||||
buffer = skeletalMesh->GetVertexBuffer();
|
||||
break;
|
||||
case AnimationType_Skeletal:
|
||||
{
|
||||
SkeletalMesh* skeletalMesh = static_cast<SkeletalMesh*>(subMesh);
|
||||
buffer = skeletalMesh->GetVertexBuffer();
|
||||
break;
|
||||
}
|
||||
|
||||
case AnimationType_Static:
|
||||
{
|
||||
StaticMesh* staticMesh = static_cast<StaticMesh*>(subMesh);
|
||||
buffer = staticMesh->GetVertexBuffer();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case nzAnimationType_Static:
|
||||
if (!buffer)
|
||||
{
|
||||
NzStaticMesh* staticMesh = static_cast<NzStaticMesh*>(subMesh);
|
||||
buffer = staticMesh->GetVertexBuffer();
|
||||
break;
|
||||
NazaraInternalError("Animation type not handled (0x" + String::Number(subMesh->GetAnimationType(), 16) + ')');
|
||||
}
|
||||
|
||||
m_mapper.Map(buffer, access);
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
VertexMapper::VertexMapper(VertexBuffer* vertexBuffer, BufferAccess access)
|
||||
{
|
||||
NazaraInternalError("Animation type not handled (0x" + NzString::Number(subMesh->GetAnimationType(), 16) + ')');
|
||||
ErrorFlags flags(ErrorFlag_ThrowException, true);
|
||||
m_mapper.Map(vertexBuffer, access);
|
||||
}
|
||||
|
||||
m_mapper.Map(buffer, access);
|
||||
}
|
||||
VertexMapper::~VertexMapper() = default;
|
||||
|
||||
NzVertexMapper::NzVertexMapper(NzVertexBuffer* vertexBuffer, nzBufferAccess access)
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
|
||||
m_mapper.Map(vertexBuffer, access);
|
||||
}
|
||||
|
||||
NzVertexMapper::~NzVertexMapper() = default;
|
||||
|
||||
void NzVertexMapper::Unmap()
|
||||
{
|
||||
m_mapper.Unmap();
|
||||
void VertexMapper::Unmap()
|
||||
{
|
||||
m_mapper.Unmap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,86 +16,89 @@
|
||||
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzVideoMode::NzVideoMode() :
|
||||
bitsPerPixel(0),
|
||||
height(0),
|
||||
width(0)
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
NzVideoMode::NzVideoMode(unsigned int w, unsigned int h, nzUInt8 bpp) :
|
||||
bitsPerPixel(bpp),
|
||||
height(h),
|
||||
width(w)
|
||||
{
|
||||
}
|
||||
|
||||
bool NzVideoMode::IsFullscreenValid() const
|
||||
{
|
||||
const std::vector<NzVideoMode>& modes = GetFullscreenModes();
|
||||
|
||||
return std::binary_search(modes.begin(), modes.end(), *this, std::greater<NzVideoMode>());
|
||||
}
|
||||
|
||||
NzVideoMode NzVideoMode::GetDesktopMode()
|
||||
{
|
||||
return NzVideoModeImpl::GetDesktopMode();
|
||||
}
|
||||
|
||||
const std::vector<NzVideoMode>& NzVideoMode::GetFullscreenModes()
|
||||
{
|
||||
static std::vector<NzVideoMode> modes;
|
||||
if (modes.empty())
|
||||
VideoMode::VideoMode() :
|
||||
bitsPerPixel(0),
|
||||
height(0),
|
||||
width(0)
|
||||
{
|
||||
NzVideoModeImpl::GetFullscreenModes(modes);
|
||||
std::sort(modes.begin(), modes.end(), std::greater<NzVideoMode>());
|
||||
}
|
||||
|
||||
return modes;
|
||||
}
|
||||
|
||||
bool operator==(const NzVideoMode& left, const NzVideoMode& right)
|
||||
{
|
||||
return left.width == right.width && left.height == right.height && left.bitsPerPixel == right.bitsPerPixel;
|
||||
}
|
||||
|
||||
bool operator!=(const NzVideoMode& left, const NzVideoMode& right)
|
||||
{
|
||||
return left.width != right.width || left.height != right.height || left.bitsPerPixel != right.bitsPerPixel;
|
||||
}
|
||||
|
||||
bool operator<(const NzVideoMode& left, const NzVideoMode& right)
|
||||
{
|
||||
if (left.bitsPerPixel == right.bitsPerPixel)
|
||||
VideoMode::VideoMode(unsigned int w, unsigned int h, UInt8 bpp) :
|
||||
bitsPerPixel(bpp),
|
||||
height(h),
|
||||
width(w)
|
||||
{
|
||||
if (left.width == right.width)
|
||||
return left.height < right.height;
|
||||
}
|
||||
|
||||
bool VideoMode::IsFullscreenValid() const
|
||||
{
|
||||
const std::vector<VideoMode>& modes = GetFullscreenModes();
|
||||
|
||||
return std::binary_search(modes.begin(), modes.end(), *this, std::greater<VideoMode>());
|
||||
}
|
||||
|
||||
VideoMode VideoMode::GetDesktopMode()
|
||||
{
|
||||
return VideoModeImpl::GetDesktopMode();
|
||||
}
|
||||
|
||||
const std::vector<VideoMode>& VideoMode::GetFullscreenModes()
|
||||
{
|
||||
static std::vector<VideoMode> modes;
|
||||
if (modes.empty())
|
||||
{
|
||||
VideoModeImpl::GetFullscreenModes(modes);
|
||||
std::sort(modes.begin(), modes.end(), std::greater<VideoMode>());
|
||||
}
|
||||
|
||||
return modes;
|
||||
}
|
||||
|
||||
bool operator==(const VideoMode& left, const VideoMode& right)
|
||||
{
|
||||
return left.width == right.width && left.height == right.height && left.bitsPerPixel == right.bitsPerPixel;
|
||||
}
|
||||
|
||||
bool operator!=(const VideoMode& left, const VideoMode& right)
|
||||
{
|
||||
return left.width != right.width || left.height != right.height || left.bitsPerPixel != right.bitsPerPixel;
|
||||
}
|
||||
|
||||
bool operator<(const VideoMode& left, const VideoMode& right)
|
||||
{
|
||||
if (left.bitsPerPixel == right.bitsPerPixel)
|
||||
{
|
||||
if (left.width == right.width)
|
||||
return left.height < right.height;
|
||||
else
|
||||
return left.width < right.width;
|
||||
}
|
||||
else
|
||||
return left.width < right.width;
|
||||
return left.bitsPerPixel < right.bitsPerPixel;
|
||||
}
|
||||
else
|
||||
return left.bitsPerPixel < right.bitsPerPixel;
|
||||
}
|
||||
|
||||
bool operator<=(const NzVideoMode& left, const NzVideoMode& right)
|
||||
{
|
||||
if (left.bitsPerPixel == right.bitsPerPixel)
|
||||
bool operator<=(const VideoMode& left, const VideoMode& right)
|
||||
{
|
||||
if (left.width == right.width)
|
||||
return left.height <= right.height;
|
||||
if (left.bitsPerPixel == right.bitsPerPixel)
|
||||
{
|
||||
if (left.width == right.width)
|
||||
return left.height <= right.height;
|
||||
else
|
||||
return left.width < right.width;
|
||||
}
|
||||
else
|
||||
return left.width < right.width;
|
||||
return left.bitsPerPixel < right.bitsPerPixel;
|
||||
}
|
||||
else
|
||||
return left.bitsPerPixel < right.bitsPerPixel;
|
||||
}
|
||||
|
||||
bool operator>(const NzVideoMode& left, const NzVideoMode& right)
|
||||
{
|
||||
return right < left;
|
||||
}
|
||||
bool operator>(const VideoMode& left, const VideoMode& right)
|
||||
{
|
||||
return right < left;
|
||||
}
|
||||
|
||||
bool operator>=(const NzVideoMode& left, const NzVideoMode& right)
|
||||
{
|
||||
return right <= left;
|
||||
bool operator>=(const VideoMode& left, const VideoMode& right)
|
||||
{
|
||||
return right <= left;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
class NzVideoMode;
|
||||
class VideoMode;
|
||||
|
||||
class NzVideoModeImpl
|
||||
class VideoModeImpl
|
||||
{
|
||||
public:
|
||||
static NzVideoMode GetDesktopMode();
|
||||
static void GetFullscreenModes(std::vector<NzVideoMode>& modes);
|
||||
static VideoMode GetDesktopMode();
|
||||
static void GetFullscreenModes(std::vector<VideoMode>& modes);
|
||||
};
|
||||
|
||||
#endif // NAZARA_VIDEOMODEIMPL_HPP
|
||||
|
||||
@@ -8,46 +8,49 @@
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
bool NzCursorImpl::Create(const NzImage& cursor, int hotSpotX, int hotSpotY)
|
||||
namespace Nz
|
||||
{
|
||||
NzImage windowsCursor(cursor);
|
||||
if (!windowsCursor.Convert(nzPixelFormat_BGRA8))
|
||||
bool CursorImpl::Create(const Image& cursor, int hotSpotX, int hotSpotY)
|
||||
{
|
||||
NazaraError("Failed to convert cursor to BGRA8");
|
||||
return false;
|
||||
Image windowsCursor(cursor);
|
||||
if (!windowsCursor.Convert(PixelFormatType_BGRA8))
|
||||
{
|
||||
NazaraError("Failed to convert cursor to BGRA8");
|
||||
return false;
|
||||
}
|
||||
|
||||
HBITMAP bitmap = CreateBitmap(windowsCursor.GetWidth(), windowsCursor.GetHeight(), 1, 32, windowsCursor.GetConstPixels());
|
||||
HBITMAP monoBitmap = CreateBitmap(windowsCursor.GetWidth(), windowsCursor.GetHeight(), 1, 1, nullptr);
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648052(v=vs.85).aspx
|
||||
ICONINFO iconInfo;
|
||||
iconInfo.fIcon = FALSE;
|
||||
iconInfo.xHotspot = hotSpotX;
|
||||
iconInfo.yHotspot = hotSpotY;
|
||||
iconInfo.hbmMask = monoBitmap;
|
||||
iconInfo.hbmColor = bitmap;
|
||||
|
||||
m_cursor = CreateIconIndirect(&iconInfo);
|
||||
|
||||
DeleteObject(bitmap);
|
||||
DeleteObject(monoBitmap);
|
||||
|
||||
if (!m_cursor)
|
||||
{
|
||||
NazaraError("Failed to create cursor: " + Error::GetLastSystemError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HBITMAP bitmap = CreateBitmap(windowsCursor.GetWidth(), windowsCursor.GetHeight(), 1, 32, windowsCursor.GetConstPixels());
|
||||
HBITMAP monoBitmap = CreateBitmap(windowsCursor.GetWidth(), windowsCursor.GetHeight(), 1, 1, nullptr);
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648052(v=vs.85).aspx
|
||||
ICONINFO iconInfo;
|
||||
iconInfo.fIcon = FALSE;
|
||||
iconInfo.xHotspot = hotSpotX;
|
||||
iconInfo.yHotspot = hotSpotY;
|
||||
iconInfo.hbmMask = monoBitmap;
|
||||
iconInfo.hbmColor = bitmap;
|
||||
|
||||
m_cursor = CreateIconIndirect(&iconInfo);
|
||||
|
||||
DeleteObject(bitmap);
|
||||
DeleteObject(monoBitmap);
|
||||
|
||||
if (!m_cursor)
|
||||
void CursorImpl::Destroy()
|
||||
{
|
||||
NazaraError("Failed to create cursor: " + NzError::GetLastSystemError());
|
||||
return false;
|
||||
DestroyIcon(m_cursor);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzCursorImpl::Destroy()
|
||||
{
|
||||
DestroyIcon(m_cursor);
|
||||
}
|
||||
|
||||
HCURSOR NzCursorImpl::GetCursor()
|
||||
{
|
||||
return m_cursor;
|
||||
HCURSOR CursorImpl::GetCursor()
|
||||
{
|
||||
return m_cursor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,18 +10,21 @@
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <windows.h>
|
||||
|
||||
class NzImage;
|
||||
|
||||
class NzCursorImpl
|
||||
namespace Nz
|
||||
{
|
||||
public:
|
||||
bool Create(const NzImage& image, int hotSpotX, int hotSpotY);
|
||||
void Destroy();
|
||||
class Image;
|
||||
|
||||
HCURSOR GetCursor();
|
||||
class CursorImpl
|
||||
{
|
||||
public:
|
||||
bool Create(const Image& image, int hotSpotX, int hotSpotY);
|
||||
void Destroy();
|
||||
|
||||
private:
|
||||
HICON m_cursor = nullptr;
|
||||
};
|
||||
HCURSOR GetCursor();
|
||||
|
||||
private:
|
||||
HICON m_cursor = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NAZARA_CURSORIMPL_HPP
|
||||
|
||||
@@ -7,44 +7,47 @@
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
bool NzIconImpl::Create(const NzImage& icon)
|
||||
namespace Nz
|
||||
{
|
||||
NzImage windowsIcon(icon); // Vive le COW
|
||||
if (!windowsIcon.Convert(nzPixelFormat_BGRA8))
|
||||
bool IconImpl::Create(const Image& icon)
|
||||
{
|
||||
NazaraError("Failed to convert icon to BGRA8");
|
||||
return false;
|
||||
Image windowsIcon(icon); // Vive le COW
|
||||
if (!windowsIcon.Convert(PixelFormatType_BGRA8))
|
||||
{
|
||||
NazaraError("Failed to convert icon to BGRA8");
|
||||
return false;
|
||||
}
|
||||
|
||||
HBITMAP bitmap = CreateBitmap(windowsIcon.GetWidth(), windowsIcon.GetHeight(), 1, 32, windowsIcon.GetConstPixels());
|
||||
HBITMAP monoBitmap = CreateBitmap(windowsIcon.GetWidth(), windowsIcon.GetHeight(), 1, 1, nullptr);
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648052(v=vs.85).aspx
|
||||
ICONINFO iconInfo;
|
||||
iconInfo.fIcon = TRUE;
|
||||
iconInfo.hbmMask = monoBitmap;
|
||||
iconInfo.hbmColor = bitmap;
|
||||
|
||||
m_icon = CreateIconIndirect(&iconInfo);
|
||||
|
||||
DeleteObject(bitmap);
|
||||
DeleteObject(monoBitmap);
|
||||
|
||||
if (!m_icon)
|
||||
{
|
||||
NazaraError("Failed to create icon: " + Error::GetLastSystemError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HBITMAP bitmap = CreateBitmap(windowsIcon.GetWidth(), windowsIcon.GetHeight(), 1, 32, windowsIcon.GetConstPixels());
|
||||
HBITMAP monoBitmap = CreateBitmap(windowsIcon.GetWidth(), windowsIcon.GetHeight(), 1, 1, nullptr);
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648052(v=vs.85).aspx
|
||||
ICONINFO iconInfo;
|
||||
iconInfo.fIcon = TRUE;
|
||||
iconInfo.hbmMask = monoBitmap;
|
||||
iconInfo.hbmColor = bitmap;
|
||||
|
||||
m_icon = CreateIconIndirect(&iconInfo);
|
||||
|
||||
DeleteObject(bitmap);
|
||||
DeleteObject(monoBitmap);
|
||||
|
||||
if (!m_icon)
|
||||
void IconImpl::Destroy()
|
||||
{
|
||||
NazaraError("Failed to create icon: " + NzError::GetLastSystemError());
|
||||
return false;
|
||||
DestroyIcon(m_icon);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzIconImpl::Destroy()
|
||||
{
|
||||
DestroyIcon(m_icon);
|
||||
}
|
||||
|
||||
HICON NzIconImpl::GetIcon()
|
||||
{
|
||||
return m_icon;
|
||||
HICON IconImpl::GetIcon()
|
||||
{
|
||||
return m_icon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,18 +10,21 @@
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <windows.h>
|
||||
|
||||
class NzImage;
|
||||
|
||||
class NzIconImpl
|
||||
namespace Nz
|
||||
{
|
||||
public:
|
||||
bool Create(const NzImage& image);
|
||||
void Destroy();
|
||||
class Image;
|
||||
|
||||
HICON GetIcon();
|
||||
class IconImpl
|
||||
{
|
||||
public:
|
||||
bool Create(const Image& image);
|
||||
void Destroy();
|
||||
|
||||
private:
|
||||
HICON m_icon = nullptr;
|
||||
};
|
||||
HICON GetIcon();
|
||||
|
||||
private:
|
||||
HICON m_icon = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NAZARA_ICONIMPL_HPP
|
||||
|
||||
@@ -8,286 +8,289 @@
|
||||
#include <windows.h>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
namespace Nz
|
||||
{
|
||||
int vKeys[NzKeyboard::Count] = {
|
||||
// Lettres
|
||||
0x41, // Key::A
|
||||
0x42, // Key::B
|
||||
0x43, // Key::C
|
||||
0x44, // Key::D
|
||||
0x45, // Key::E
|
||||
0x46, // Key::F
|
||||
0x47, // Key::G
|
||||
0x48, // Key::H
|
||||
0x49, // Key::I
|
||||
0x4A, // Key::J
|
||||
0x4B, // Key::K
|
||||
0x4C, // Key::L
|
||||
0x4D, // Key::M
|
||||
0x4E, // Key::N
|
||||
0x4F, // Key::O
|
||||
0x50, // Key::P
|
||||
0x51, // Key::Q
|
||||
0x52, // Key::R
|
||||
0x53, // Key::S
|
||||
0x54, // Key::T
|
||||
0x55, // Key::U
|
||||
0x56, // Key::V
|
||||
0x57, // Key::W
|
||||
0x58, // Key::X
|
||||
0x59, // Key::Y
|
||||
0x5A, // Key::Z
|
||||
|
||||
// Touches de fonction
|
||||
VK_F1, // Key::F1
|
||||
VK_F2, // Key::F2
|
||||
VK_F3, // Key::F3
|
||||
VK_F4, // Key::F4
|
||||
VK_F5, // Key::F5
|
||||
VK_F6, // Key::F6
|
||||
VK_F7, // Key::F7
|
||||
VK_F8, // Key::F8
|
||||
VK_F9, // Key::F9
|
||||
VK_F10, // Key::F10
|
||||
VK_F11, // Key::F11
|
||||
VK_F12, // Key::F12
|
||||
VK_F13, // Key::F13
|
||||
VK_F14, // Key::F14
|
||||
VK_F15, // Key::F15
|
||||
|
||||
// Flèches directionnelles
|
||||
VK_DOWN, // Key::Down
|
||||
VK_LEFT, // Key::Left
|
||||
VK_RIGHT, // Key::Right
|
||||
VK_UP, // Key::Up
|
||||
|
||||
// Pavé numérique
|
||||
VK_ADD, // Key::Add
|
||||
VK_DECIMAL, // Key::Decimal
|
||||
VK_DIVIDE, // Key::Divide
|
||||
VK_MULTIPLY, // Key::Multiply
|
||||
VK_NUMPAD0, // Key::Numpad0
|
||||
VK_NUMPAD1, // Key::Numpad1
|
||||
VK_NUMPAD2, // Key::Numpad2
|
||||
VK_NUMPAD3, // Key::Numpad3
|
||||
VK_NUMPAD4, // Key::Numpad4
|
||||
VK_NUMPAD5, // Key::Numpad5
|
||||
VK_NUMPAD6, // Key::Numpad6
|
||||
VK_NUMPAD7, // Key::Numpad7
|
||||
VK_NUMPAD8, // Key::Numpad8
|
||||
VK_NUMPAD9, // Key::Numpad9
|
||||
VK_SUBTRACT, // Key::Subtract
|
||||
|
||||
// Diverss
|
||||
VK_OEM_5, // Key::Backslash
|
||||
VK_BACK, // Key::Backspace
|
||||
VK_CLEAR, // Key::Clear
|
||||
VK_OEM_COMMA, // Key::Comma,
|
||||
VK_OEM_MINUS, // Key::Dash
|
||||
VK_DELETE, // Key::Delete
|
||||
VK_END, // Key::End
|
||||
VK_OEM_PLUS, // Key::Equal
|
||||
VK_ESCAPE, // Key::Escape
|
||||
VK_HOME, // Key::Home
|
||||
VK_INSERT, // Key::Insert
|
||||
VK_LMENU, // Key::LAlt
|
||||
VK_OEM_4, // Key::LBracket
|
||||
VK_LCONTROL, // Key::LControl
|
||||
VK_LSHIFT, // Key::LShift
|
||||
VK_LWIN, // Key::LSystem
|
||||
0x30, // Key::Num0
|
||||
0x31, // Key::Num1
|
||||
0x32, // Key::Num2
|
||||
0x33, // Key::Num3
|
||||
0x34, // Key::Num4
|
||||
0x35, // Key::Num5
|
||||
0x36, // Key::Num6
|
||||
0x37, // Key::Num7
|
||||
0x38, // Key::Num8
|
||||
0x39, // Key::Num9
|
||||
VK_NEXT, // Key::PageDown
|
||||
VK_PRIOR, // Key::PageUp
|
||||
VK_PAUSE, // Key::Pause
|
||||
VK_OEM_PERIOD, // Key::Period
|
||||
VK_PRINT, // Key::Print
|
||||
VK_SNAPSHOT, // Key::PrintScreen
|
||||
VK_OEM_7, // Key::Quote
|
||||
VK_RMENU, // Key::RAlt
|
||||
VK_OEM_6, // Key::RBracket
|
||||
VK_RCONTROL, // Key::RControl
|
||||
VK_RETURN, // Key::Return
|
||||
VK_RSHIFT, // Key::RShift
|
||||
VK_RWIN, // Key::RSystem
|
||||
VK_OEM_1, // Key::Semicolon
|
||||
VK_OEM_2, // Key::Slash
|
||||
VK_SPACE, // Key::Space
|
||||
VK_TAB, // Key::Tab
|
||||
VK_OEM_3, // Key::Tilde
|
||||
|
||||
// Touches navigateur
|
||||
VK_BROWSER_BACK, // Key::Browser_Back
|
||||
VK_BROWSER_FAVORITES, // Key::Browser_Favorites
|
||||
VK_BROWSER_FORWARD, // Key::Browser_Forward
|
||||
VK_BROWSER_HOME, // Key::Browser_Home
|
||||
VK_BROWSER_REFRESH, // Key::Browser_Refresh
|
||||
VK_BROWSER_SEARCH, // Key::Browser_Search
|
||||
VK_BROWSER_STOP, // Key::Browser_Stop
|
||||
|
||||
// Touches de contrôle
|
||||
VK_MEDIA_NEXT_TRACK, // Key::Media_Next,
|
||||
VK_MEDIA_PLAY_PAUSE, // Key::Media_PlayPause,
|
||||
VK_MEDIA_PREV_TRACK, // Key::Media_Previous,
|
||||
VK_MEDIA_STOP, // Key::Media_Stop,
|
||||
|
||||
// Touches de contrôle du volume
|
||||
VK_VOLUME_DOWN, // Key::Volume_Down
|
||||
VK_VOLUME_MUTE, // Key::Volume_Mute
|
||||
VK_VOLUME_UP, // Key::Volume_Up
|
||||
|
||||
// Touches à verrouillage
|
||||
VK_CAPITAL, // Key::CapsLock
|
||||
VK_NUMLOCK, // Key::NumLock
|
||||
VK_SCROLL // Key::ScrollLock
|
||||
};
|
||||
}
|
||||
|
||||
NzString NzEventImpl::GetKeyName(NzKeyboard::Key key)
|
||||
{
|
||||
// http://www.ffuts.org/blog/mapvirtualkey-getkeynametext-and-a-story-of-how-to/
|
||||
int vk = vKeys[key];
|
||||
unsigned int code = MapVirtualKeyW(vk, 0) << 16;
|
||||
|
||||
///FIXME: Liste complète ?
|
||||
switch (vk)
|
||||
namespace
|
||||
{
|
||||
case VK_ATTN:
|
||||
case VK_DOWN:
|
||||
case VK_DECIMAL:
|
||||
case VK_DELETE:
|
||||
case VK_DIVIDE:
|
||||
case VK_END:
|
||||
case VK_HOME:
|
||||
case VK_INSERT:
|
||||
case VK_LEFT:
|
||||
case VK_LWIN:
|
||||
case VK_OEM_1:
|
||||
case VK_OEM_2:
|
||||
case VK_OEM_3:
|
||||
case VK_OEM_4:
|
||||
case VK_OEM_5:
|
||||
case VK_OEM_6:
|
||||
case VK_OEM_7:
|
||||
case VK_OEM_CLEAR:
|
||||
case VK_OEM_COMMA:
|
||||
case VK_OEM_MINUS:
|
||||
case VK_OEM_PERIOD:
|
||||
case VK_OEM_PLUS:
|
||||
case VK_PAUSE:
|
||||
case VK_NEXT:
|
||||
case VK_NUMLOCK:
|
||||
case VK_PRIOR:
|
||||
case VK_RIGHT:
|
||||
case VK_RWIN:
|
||||
case VK_UP:
|
||||
code |= 0x1000000; // 24ème bit pour l'extension
|
||||
break;
|
||||
int vKeys[Keyboard::Count] = {
|
||||
// Lettres
|
||||
0x41, // Key::A
|
||||
0x42, // Key::B
|
||||
0x43, // Key::C
|
||||
0x44, // Key::D
|
||||
0x45, // Key::E
|
||||
0x46, // Key::F
|
||||
0x47, // Key::G
|
||||
0x48, // Key::H
|
||||
0x49, // Key::I
|
||||
0x4A, // Key::J
|
||||
0x4B, // Key::K
|
||||
0x4C, // Key::L
|
||||
0x4D, // Key::M
|
||||
0x4E, // Key::N
|
||||
0x4F, // Key::O
|
||||
0x50, // Key::P
|
||||
0x51, // Key::Q
|
||||
0x52, // Key::R
|
||||
0x53, // Key::S
|
||||
0x54, // Key::T
|
||||
0x55, // Key::U
|
||||
0x56, // Key::V
|
||||
0x57, // Key::W
|
||||
0x58, // Key::X
|
||||
0x59, // Key::Y
|
||||
0x5A, // Key::Z
|
||||
|
||||
// Touches de fonction
|
||||
VK_F1, // Key::F1
|
||||
VK_F2, // Key::F2
|
||||
VK_F3, // Key::F3
|
||||
VK_F4, // Key::F4
|
||||
VK_F5, // Key::F5
|
||||
VK_F6, // Key::F6
|
||||
VK_F7, // Key::F7
|
||||
VK_F8, // Key::F8
|
||||
VK_F9, // Key::F9
|
||||
VK_F10, // Key::F10
|
||||
VK_F11, // Key::F11
|
||||
VK_F12, // Key::F12
|
||||
VK_F13, // Key::F13
|
||||
VK_F14, // Key::F14
|
||||
VK_F15, // Key::F15
|
||||
|
||||
// Flèches directionnelles
|
||||
VK_DOWN, // Key::Down
|
||||
VK_LEFT, // Key::Left
|
||||
VK_RIGHT, // Key::Right
|
||||
VK_UP, // Key::Up
|
||||
|
||||
// Pavé numérique
|
||||
VK_ADD, // Key::Add
|
||||
VK_DECIMAL, // Key::Decimal
|
||||
VK_DIVIDE, // Key::Divide
|
||||
VK_MULTIPLY, // Key::Multiply
|
||||
VK_NUMPAD0, // Key::Numpad0
|
||||
VK_NUMPAD1, // Key::Numpad1
|
||||
VK_NUMPAD2, // Key::Numpad2
|
||||
VK_NUMPAD3, // Key::Numpad3
|
||||
VK_NUMPAD4, // Key::Numpad4
|
||||
VK_NUMPAD5, // Key::Numpad5
|
||||
VK_NUMPAD6, // Key::Numpad6
|
||||
VK_NUMPAD7, // Key::Numpad7
|
||||
VK_NUMPAD8, // Key::Numpad8
|
||||
VK_NUMPAD9, // Key::Numpad9
|
||||
VK_SUBTRACT, // Key::Subtract
|
||||
|
||||
// Diverss
|
||||
VK_OEM_5, // Key::Backslash
|
||||
VK_BACK, // Key::Backspace
|
||||
VK_CLEAR, // Key::Clear
|
||||
VK_OEM_COMMA, // Key::Comma,
|
||||
VK_OEM_MINUS, // Key::Dash
|
||||
VK_DELETE, // Key::Delete
|
||||
VK_END, // Key::End
|
||||
VK_OEM_PLUS, // Key::Equal
|
||||
VK_ESCAPE, // Key::Escape
|
||||
VK_HOME, // Key::Home
|
||||
VK_INSERT, // Key::Insert
|
||||
VK_LMENU, // Key::LAlt
|
||||
VK_OEM_4, // Key::LBracket
|
||||
VK_LCONTROL, // Key::LControl
|
||||
VK_LSHIFT, // Key::LShift
|
||||
VK_LWIN, // Key::LSystem
|
||||
0x30, // Key::Num0
|
||||
0x31, // Key::Num1
|
||||
0x32, // Key::Num2
|
||||
0x33, // Key::Num3
|
||||
0x34, // Key::Num4
|
||||
0x35, // Key::Num5
|
||||
0x36, // Key::Num6
|
||||
0x37, // Key::Num7
|
||||
0x38, // Key::Num8
|
||||
0x39, // Key::Num9
|
||||
VK_NEXT, // Key::PageDown
|
||||
VK_PRIOR, // Key::PageUp
|
||||
VK_PAUSE, // Key::Pause
|
||||
VK_OEM_PERIOD, // Key::Period
|
||||
VK_PRINT, // Key::Print
|
||||
VK_SNAPSHOT, // Key::PrintScreen
|
||||
VK_OEM_7, // Key::Quote
|
||||
VK_RMENU, // Key::RAlt
|
||||
VK_OEM_6, // Key::RBracket
|
||||
VK_RCONTROL, // Key::RControl
|
||||
VK_RETURN, // Key::Return
|
||||
VK_RSHIFT, // Key::RShift
|
||||
VK_RWIN, // Key::RSystem
|
||||
VK_OEM_1, // Key::Semicolon
|
||||
VK_OEM_2, // Key::Slash
|
||||
VK_SPACE, // Key::Space
|
||||
VK_TAB, // Key::Tab
|
||||
VK_OEM_3, // Key::Tilde
|
||||
|
||||
// Touches navigateur
|
||||
VK_BROWSER_BACK, // Key::Browser_Back
|
||||
VK_BROWSER_FAVORITES, // Key::Browser_Favorites
|
||||
VK_BROWSER_FORWARD, // Key::Browser_Forward
|
||||
VK_BROWSER_HOME, // Key::Browser_Home
|
||||
VK_BROWSER_REFRESH, // Key::Browser_Refresh
|
||||
VK_BROWSER_SEARCH, // Key::Browser_Search
|
||||
VK_BROWSER_STOP, // Key::Browser_Stop
|
||||
|
||||
// Touches de contrôle
|
||||
VK_MEDIA_NEXT_TRACK, // Key::Media_Next,
|
||||
VK_MEDIA_PLAY_PAUSE, // Key::Media_PlayPause,
|
||||
VK_MEDIA_PREV_TRACK, // Key::Media_Previous,
|
||||
VK_MEDIA_STOP, // Key::Media_Stop,
|
||||
|
||||
// Touches de contrôle du volume
|
||||
VK_VOLUME_DOWN, // Key::Volume_Down
|
||||
VK_VOLUME_MUTE, // Key::Volume_Mute
|
||||
VK_VOLUME_UP, // Key::Volume_Up
|
||||
|
||||
// Touches à verrouillage
|
||||
VK_CAPITAL, // Key::CapsLock
|
||||
VK_NUMLOCK, // Key::NumLock
|
||||
VK_SCROLL // Key::ScrollLock
|
||||
};
|
||||
}
|
||||
|
||||
wchar_t keyName[20]; // Je ne pense pas que ça dépassera 20 caractères
|
||||
if (!GetKeyNameTextW(code, &keyName[0], 20))
|
||||
return "Unknown";
|
||||
String EventImpl::GetKeyName(Keyboard::Key key)
|
||||
{
|
||||
// http://www.ffuts.org/blog/mapvirtualkey-getkeynametext-and-a-story-of-how-to/
|
||||
int vk = vKeys[key];
|
||||
unsigned int code = MapVirtualKeyW(vk, 0) << 16;
|
||||
|
||||
return NzString::Unicode(keyName);
|
||||
}
|
||||
///FIXME: Liste complète ?
|
||||
switch (vk)
|
||||
{
|
||||
case VK_ATTN:
|
||||
case VK_DOWN:
|
||||
case VK_DECIMAL:
|
||||
case VK_DELETE:
|
||||
case VK_DIVIDE:
|
||||
case VK_END:
|
||||
case VK_HOME:
|
||||
case VK_INSERT:
|
||||
case VK_LEFT:
|
||||
case VK_LWIN:
|
||||
case VK_OEM_1:
|
||||
case VK_OEM_2:
|
||||
case VK_OEM_3:
|
||||
case VK_OEM_4:
|
||||
case VK_OEM_5:
|
||||
case VK_OEM_6:
|
||||
case VK_OEM_7:
|
||||
case VK_OEM_CLEAR:
|
||||
case VK_OEM_COMMA:
|
||||
case VK_OEM_MINUS:
|
||||
case VK_OEM_PERIOD:
|
||||
case VK_OEM_PLUS:
|
||||
case VK_PAUSE:
|
||||
case VK_NEXT:
|
||||
case VK_NUMLOCK:
|
||||
case VK_PRIOR:
|
||||
case VK_RIGHT:
|
||||
case VK_RWIN:
|
||||
case VK_UP:
|
||||
code |= 0x1000000; // 24ème bit pour l'extension
|
||||
break;
|
||||
}
|
||||
|
||||
NzVector2i NzEventImpl::GetMousePosition()
|
||||
{
|
||||
POINT pos;
|
||||
GetCursorPos(&pos);
|
||||
wchar_t keyName[20]; // Je ne pense pas que ça dépassera 20 caractères
|
||||
if (!GetKeyNameTextW(code, &keyName[0], 20))
|
||||
return "Unknown";
|
||||
|
||||
return NzVector2i(pos.x, pos.y);
|
||||
}
|
||||
return String::Unicode(keyName);
|
||||
}
|
||||
|
||||
NzVector2i NzEventImpl::GetMousePosition(const NzWindow& relativeTo)
|
||||
{
|
||||
HWND handle = reinterpret_cast<HWND>(relativeTo.GetHandle());
|
||||
if (handle)
|
||||
Vector2i EventImpl::GetMousePosition()
|
||||
{
|
||||
POINT pos;
|
||||
GetCursorPos(&pos);
|
||||
ScreenToClient(handle, &pos);
|
||||
|
||||
return NzVector2i(pos.x, pos.y);
|
||||
return Vector2i(pos.x, pos.y);
|
||||
}
|
||||
else
|
||||
|
||||
Vector2i EventImpl::GetMousePosition(const Window& relativeTo)
|
||||
{
|
||||
NazaraError("Invalid window handle");
|
||||
|
||||
// Attention que (-1, -1) est une position tout à fait valide et ne doit pas servir de test
|
||||
return NzVector2i(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
bool NzEventImpl::IsKeyPressed(NzKeyboard::Key key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case NzKeyboard::CapsLock:
|
||||
case NzKeyboard::NumLock:
|
||||
case NzKeyboard::ScrollLock:
|
||||
return GetKeyState(vKeys[key]) != 0;
|
||||
|
||||
default:
|
||||
return (GetAsyncKeyState(vKeys[key]) & 0x8000) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool NzEventImpl::IsMouseButtonPressed(NzMouse::Button button)
|
||||
{
|
||||
static int vButtons[NzMouse::Max+1] = {
|
||||
VK_LBUTTON, // Button::Left
|
||||
VK_MBUTTON, // Button::Middle
|
||||
VK_RBUTTON, // Button::Right
|
||||
VK_XBUTTON1, // Button::XButton1
|
||||
VK_XBUTTON2 // Button::XButton2
|
||||
};
|
||||
|
||||
// Gestion de l'inversement des boutons de la souris
|
||||
if (GetSystemMetrics(SM_SWAPBUTTON))
|
||||
{
|
||||
switch (button)
|
||||
HWND handle = reinterpret_cast<HWND>(relativeTo.GetHandle());
|
||||
if (handle)
|
||||
{
|
||||
case NzMouse::Left:
|
||||
button = NzMouse::Right;
|
||||
break;
|
||||
POINT pos;
|
||||
GetCursorPos(&pos);
|
||||
ScreenToClient(handle, &pos);
|
||||
|
||||
case NzMouse::Right:
|
||||
button = NzMouse::Left;
|
||||
break;
|
||||
return Vector2i(pos.x, pos.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
NazaraError("Invalid window handle");
|
||||
|
||||
default:
|
||||
break;
|
||||
// Attention que (-1, -1) est une position tout à fait valide et ne doit pas servir de test
|
||||
return Vector2i(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
return (GetAsyncKeyState(vButtons[button]) & 0x8000) != 0;
|
||||
}
|
||||
|
||||
void NzEventImpl::SetMousePosition(int x, int y)
|
||||
{
|
||||
SetCursorPos(x, y);
|
||||
}
|
||||
|
||||
void NzEventImpl::SetMousePosition(int x, int y, const NzWindow& relativeTo)
|
||||
{
|
||||
HWND handle = reinterpret_cast<HWND>(relativeTo.GetHandle());
|
||||
if (handle)
|
||||
bool EventImpl::IsKeyPressed(Keyboard::Key key)
|
||||
{
|
||||
POINT pos = {x, y};
|
||||
ClientToScreen(handle, &pos);
|
||||
SetCursorPos(pos.x, pos.y);
|
||||
switch (key)
|
||||
{
|
||||
case Keyboard::CapsLock:
|
||||
case Keyboard::NumLock:
|
||||
case Keyboard::ScrollLock:
|
||||
return GetKeyState(vKeys[key]) != 0;
|
||||
|
||||
default:
|
||||
return (GetAsyncKeyState(vKeys[key]) & 0x8000) != 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
NazaraError("Invalid window handle");
|
||||
|
||||
bool EventImpl::IsMouseButtonPressed(Mouse::Button button)
|
||||
{
|
||||
static int vButtons[Mouse::Max+1] = {
|
||||
VK_LBUTTON, // Button::Left
|
||||
VK_MBUTTON, // Button::Middle
|
||||
VK_RBUTTON, // Button::Right
|
||||
VK_XBUTTON1, // Button::XButton1
|
||||
VK_XBUTTON2 // Button::XButton2
|
||||
};
|
||||
|
||||
// Gestion de l'inversement des boutons de la souris
|
||||
if (GetSystemMetrics(SM_SWAPBUTTON))
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case Mouse::Left:
|
||||
button = Mouse::Right;
|
||||
break;
|
||||
|
||||
case Mouse::Right:
|
||||
button = Mouse::Left;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (GetAsyncKeyState(vButtons[button]) & 0x8000) != 0;
|
||||
}
|
||||
|
||||
void EventImpl::SetMousePosition(int x, int y)
|
||||
{
|
||||
SetCursorPos(x, y);
|
||||
}
|
||||
|
||||
void EventImpl::SetMousePosition(int x, int y, const Window& relativeTo)
|
||||
{
|
||||
HWND handle = reinterpret_cast<HWND>(relativeTo.GetHandle());
|
||||
if (handle)
|
||||
{
|
||||
POINT pos = {x, y};
|
||||
ClientToScreen(handle, &pos);
|
||||
SetCursorPos(pos.x, pos.y);
|
||||
}
|
||||
else
|
||||
NazaraError("Invalid window handle");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,16 +12,19 @@
|
||||
#include <Nazara/Utility/Keyboard.hpp>
|
||||
#include <Nazara/Utility/Mouse.hpp>
|
||||
|
||||
class NzEventImpl
|
||||
namespace Nz
|
||||
{
|
||||
public:
|
||||
static NzString GetKeyName(NzKeyboard::Key key);
|
||||
static NzVector2i GetMousePosition();
|
||||
static NzVector2i GetMousePosition(const NzWindow& relativeTo);
|
||||
static bool IsKeyPressed(NzKeyboard::Key key);
|
||||
static bool IsMouseButtonPressed(NzMouse::Button button);
|
||||
static void SetMousePosition(int x, int y);
|
||||
static void SetMousePosition(int x, int y, const NzWindow& relativeTo);
|
||||
};
|
||||
class EventImpl
|
||||
{
|
||||
public:
|
||||
static String GetKeyName(Keyboard::Key key);
|
||||
static Vector2i GetMousePosition();
|
||||
static Vector2i GetMousePosition(const Window& relativeTo);
|
||||
static bool IsKeyPressed(Keyboard::Key key);
|
||||
static bool IsMouseButtonPressed(Mouse::Button button);
|
||||
static void SetMousePosition(int x, int y);
|
||||
static void SetMousePosition(int x, int y, const Window& relativeTo);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NAZARA_INPUTIMPL_HPP
|
||||
|
||||
@@ -8,25 +8,28 @@
|
||||
#include <windows.h>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzVideoMode NzVideoModeImpl::GetDesktopMode()
|
||||
namespace Nz
|
||||
{
|
||||
DEVMODE mode;
|
||||
mode.dmSize = sizeof(DEVMODE);
|
||||
EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &mode);
|
||||
|
||||
return NzVideoMode(mode.dmPelsWidth, mode.dmPelsHeight, static_cast<nzUInt8>(mode.dmBitsPerPel));
|
||||
}
|
||||
|
||||
void NzVideoModeImpl::GetFullscreenModes(std::vector<NzVideoMode>& modes)
|
||||
{
|
||||
DEVMODE win32Mode;
|
||||
win32Mode.dmSize = sizeof(DEVMODE);
|
||||
for (unsigned int i = 0; EnumDisplaySettings(nullptr, i, &win32Mode); ++i)
|
||||
VideoMode VideoModeImpl::GetDesktopMode()
|
||||
{
|
||||
NzVideoMode mode(win32Mode.dmPelsWidth, win32Mode.dmPelsHeight, static_cast<nzUInt8>(win32Mode.dmBitsPerPel));
|
||||
DEVMODE mode;
|
||||
mode.dmSize = sizeof(DEVMODE);
|
||||
EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &mode);
|
||||
|
||||
// Il existe plusieurs modes avec ces trois caractéristques identiques
|
||||
if (std::find(modes.begin(), modes.end(), mode) == modes.end())
|
||||
modes.push_back(mode);
|
||||
return VideoMode(mode.dmPelsWidth, mode.dmPelsHeight, static_cast<UInt8>(mode.dmBitsPerPel));
|
||||
}
|
||||
|
||||
void VideoModeImpl::GetFullscreenModes(std::vector<VideoMode>& modes)
|
||||
{
|
||||
DEVMODE win32Mode;
|
||||
win32Mode.dmSize = sizeof(DEVMODE);
|
||||
for (unsigned int i = 0; EnumDisplaySettings(nullptr, i, &win32Mode); ++i)
|
||||
{
|
||||
VideoMode mode(win32Mode.dmPelsWidth, win32Mode.dmPelsHeight, static_cast<UInt8>(win32Mode.dmBitsPerPel));
|
||||
|
||||
// Il existe plusieurs modes avec ces trois caractéristques identiques
|
||||
if (std::find(modes.begin(), modes.end(), mode) == modes.end())
|
||||
modes.push_back(mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,14 @@
|
||||
|
||||
#include <Nazara/Utility/VideoMode.hpp>
|
||||
|
||||
class NzVideoModeImpl
|
||||
namespace Nz
|
||||
{
|
||||
public:
|
||||
static NzVideoMode GetDesktopMode();
|
||||
static void GetFullscreenModes(std::vector<NzVideoMode>& modes);
|
||||
};
|
||||
class VideoModeImpl
|
||||
{
|
||||
public:
|
||||
static VideoMode GetDesktopMode();
|
||||
static void GetFullscreenModes(std::vector<VideoMode>& modes);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NNAZARA_VIDEOMODEIMPL_HPP
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,6 @@
|
||||
#define NAZARA_WINDOWIMPL_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <Nazara/Core/NonCopyable.hpp>
|
||||
#include <Nazara/Core/String.hpp>
|
||||
#include <Nazara/Core/Thread.hpp>
|
||||
#include <Nazara/Math/Vector2.hpp>
|
||||
@@ -21,95 +20,104 @@
|
||||
#include <Nazara/Utility/Window.hpp>
|
||||
#include <windows.h>
|
||||
|
||||
#if NAZARA_UTILITY_THREADED_WINDOW
|
||||
class NzConditionVariable;
|
||||
class NzMutex;
|
||||
#endif
|
||||
class NzWindow;
|
||||
|
||||
#undef IsMinimized // Conflit avec la méthode du même nom
|
||||
|
||||
class NzWindowImpl : NzNonCopyable
|
||||
namespace Nz
|
||||
{
|
||||
public:
|
||||
NzWindowImpl(NzWindow* parent);
|
||||
~NzWindowImpl() = default;
|
||||
#if NAZARA_UTILITY_THREADED_WINDOW
|
||||
class ConditionVariable;
|
||||
class Mutex;
|
||||
#endif
|
||||
class Window;
|
||||
|
||||
bool Create(const NzVideoMode& mode, const NzString& title, nzUInt32 style);
|
||||
bool Create(NzWindowHandle handle);
|
||||
#undef IsMinimized // Conflit avec la méthode du même nom
|
||||
|
||||
void Destroy();
|
||||
class WindowImpl
|
||||
{
|
||||
public:
|
||||
WindowImpl(Window* parent);
|
||||
WindowImpl(const WindowImpl&) = delete;
|
||||
WindowImpl(WindowImpl&&) = delete; ///TODO?
|
||||
~WindowImpl() = default;
|
||||
|
||||
void EnableKeyRepeat(bool enable);
|
||||
void EnableSmoothScrolling(bool enable);
|
||||
bool Create(const VideoMode& mode, const String& title, UInt32 style);
|
||||
bool Create(WindowHandle handle);
|
||||
|
||||
NzWindowHandle GetHandle() const;
|
||||
unsigned int GetHeight() const;
|
||||
NzVector2i GetPosition() const;
|
||||
NzVector2ui GetSize() const;
|
||||
nzUInt32 GetStyle() const;
|
||||
NzString GetTitle() const;
|
||||
unsigned int GetWidth() const;
|
||||
void Destroy();
|
||||
|
||||
bool HasFocus() const;
|
||||
void EnableKeyRepeat(bool enable);
|
||||
void EnableSmoothScrolling(bool enable);
|
||||
|
||||
void IgnoreNextMouseEvent(int mouseX, int mouseY);
|
||||
WindowHandle GetHandle() const;
|
||||
unsigned int GetHeight() const;
|
||||
Vector2i GetPosition() const;
|
||||
Vector2ui GetSize() const;
|
||||
UInt32 GetStyle() const;
|
||||
String GetTitle() const;
|
||||
unsigned int GetWidth() const;
|
||||
|
||||
bool IsMinimized() const;
|
||||
bool IsVisible() const;
|
||||
bool HasFocus() const;
|
||||
|
||||
void ProcessEvents(bool block);
|
||||
void IgnoreNextMouseEvent(int mouseX, int mouseY);
|
||||
|
||||
void SetCursor(nzWindowCursor cursor);
|
||||
void SetCursor(const NzCursor& cursor);
|
||||
void SetEventListener(bool listener);
|
||||
void SetFocus();
|
||||
void SetIcon(const NzIcon& icon);
|
||||
void SetMaximumSize(int width, int height);
|
||||
void SetMinimumSize(int width, int height);
|
||||
void SetPosition(int x, int y);
|
||||
void SetSize(unsigned int width, unsigned int height);
|
||||
void SetStayOnTop(bool stayOnTop);
|
||||
void SetTitle(const NzString& title);
|
||||
void SetVisible(bool visible);
|
||||
bool IsMinimized() const;
|
||||
bool IsVisible() const;
|
||||
|
||||
static bool Initialize();
|
||||
static void Uninitialize();
|
||||
void ProcessEvents(bool block);
|
||||
|
||||
private:
|
||||
bool HandleMessage(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
void SetCursor(WindowCursor cursor);
|
||||
void SetCursor(const Cursor& cursor);
|
||||
void SetEventListener(bool listener);
|
||||
void SetFocus();
|
||||
void SetIcon(const Icon& icon);
|
||||
void SetMaximumSize(int width, int height);
|
||||
void SetMinimumSize(int width, int height);
|
||||
void SetPosition(int x, int y);
|
||||
void SetSize(unsigned int width, unsigned int height);
|
||||
void SetStayOnTop(bool stayOnTop);
|
||||
void SetTitle(const String& title);
|
||||
void SetVisible(bool visible);
|
||||
|
||||
static NzKeyboard::Key ConvertVirtualKey(WPARAM key, LPARAM flags);
|
||||
static LRESULT CALLBACK MessageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static nzUInt32 RetrieveStyle(HWND window);
|
||||
#if NAZARA_UTILITY_THREADED_WINDOW
|
||||
static void WindowThread(HWND* handle, DWORD styleEx, const wchar_t* title, DWORD style, unsigned int x, unsigned int y, unsigned int width, unsigned int height, NzWindowImpl* window, NzMutex* mutex, NzConditionVariable* condition);
|
||||
#endif
|
||||
WindowImpl& operator=(const WindowImpl&) = delete;
|
||||
WindowImpl& operator=(WindowImpl&&) = delete; ///TODO?
|
||||
|
||||
static bool Initialize();
|
||||
static void Uninitialize();
|
||||
|
||||
private:
|
||||
bool HandleMessage(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
static Keyboard::Key ConvertVirtualKey(WPARAM key, LPARAM flags);
|
||||
static LRESULT CALLBACK MessageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static UInt32 RetrieveStyle(HWND window);
|
||||
#if NAZARA_UTILITY_THREADED_WINDOW
|
||||
static void WindowThread(HWND* handle, DWORD styleEx, const wchar_t* title, DWORD style, unsigned int x, unsigned int y, unsigned int width, unsigned int height, WindowImpl* window, Mutex* mutex, ConditionVariable* condition);
|
||||
#endif
|
||||
|
||||
HCURSOR m_cursor;
|
||||
HWND m_handle;
|
||||
LONG_PTR m_callback;
|
||||
UInt32 m_style;
|
||||
Vector2i m_maxSize;
|
||||
Vector2i m_minSize;
|
||||
Vector2i m_mousePos;
|
||||
Vector2i m_position;
|
||||
Vector2ui m_size;
|
||||
#if NAZARA_UTILITY_THREADED_WINDOW
|
||||
Thread m_thread;
|
||||
#endif
|
||||
Window* m_parent;
|
||||
bool m_eventListener;
|
||||
bool m_keyRepeat;
|
||||
bool m_mouseInside;
|
||||
bool m_ownsWindow;
|
||||
#if !NAZARA_UTILITY_THREADED_WINDOW
|
||||
bool m_sizemove;
|
||||
#endif
|
||||
bool m_smoothScrolling;
|
||||
#if NAZARA_UTILITY_THREADED_WINDOW
|
||||
bool m_threadActive;
|
||||
#endif
|
||||
short m_scrolling;
|
||||
};
|
||||
}
|
||||
|
||||
HCURSOR m_cursor;
|
||||
HWND m_handle;
|
||||
LONG_PTR m_callback;
|
||||
nzUInt32 m_style;
|
||||
NzVector2i m_maxSize;
|
||||
NzVector2i m_minSize;
|
||||
NzVector2i m_mousePos;
|
||||
NzVector2i m_position;
|
||||
NzVector2ui m_size;
|
||||
#if NAZARA_UTILITY_THREADED_WINDOW
|
||||
NzThread m_thread;
|
||||
#endif
|
||||
NzWindow* m_parent;
|
||||
bool m_eventListener;
|
||||
bool m_keyRepeat;
|
||||
bool m_mouseInside;
|
||||
bool m_ownsWindow;
|
||||
#if !NAZARA_UTILITY_THREADED_WINDOW
|
||||
bool m_sizemove;
|
||||
#endif
|
||||
bool m_smoothScrolling;
|
||||
#if NAZARA_UTILITY_THREADED_WINDOW
|
||||
bool m_threadActive;
|
||||
#endif
|
||||
short m_scrolling;
|
||||
};
|
||||
#endif // NAZARA_WINDOWIMPL_HPP
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,40 +5,43 @@
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
template <typename T>
|
||||
NzScopedXCB<T>::NzScopedXCB(T* pointer) :
|
||||
m_pointer(pointer)
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
template <typename T>
|
||||
ScopedXCB<T>::ScopedXCB(T* pointer) :
|
||||
m_pointer(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
NzScopedXCB<T>::~NzScopedXCB()
|
||||
{
|
||||
std::free(m_pointer);
|
||||
}
|
||||
template <typename T>
|
||||
ScopedXCB<T>::~ScopedXCB()
|
||||
{
|
||||
std::free(m_pointer);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* NzScopedXCB<T>::operator ->() const
|
||||
{
|
||||
return m_pointer;
|
||||
}
|
||||
template <typename T>
|
||||
T* ScopedXCB<T>::operator ->() const
|
||||
{
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T** NzScopedXCB<T>::operator &()
|
||||
{
|
||||
return &m_pointer;
|
||||
}
|
||||
template <typename T>
|
||||
T** ScopedXCB<T>::operator &()
|
||||
{
|
||||
return &m_pointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
NzScopedXCB<T>::operator bool() const
|
||||
{
|
||||
return m_pointer != nullptr;
|
||||
}
|
||||
template <typename T>
|
||||
ScopedXCB<T>::operator bool() const
|
||||
{
|
||||
return m_pointer != nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* NzScopedXCB<T>::get() const
|
||||
{
|
||||
return m_pointer;
|
||||
template <typename T>
|
||||
T* ScopedXCB<T>::get() const
|
||||
{
|
||||
return m_pointer;
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Utility/DebugOff.hpp>
|
||||
|
||||
Reference in New Issue
Block a user