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:
Lynix
2015-09-25 23:16:58 +02:00
613 changed files with 68051 additions and 66125 deletions

View File

@@ -5,7 +5,10 @@
#include <Nazara/Utility/AbstractAtlas.hpp>
#include <Nazara/Utility/Debug.hpp>
NzAbstractAtlas::~NzAbstractAtlas()
namespace Nz
{
OnAtlasRelease(this);
AbstractAtlas::~AbstractAtlas()
{
OnAtlasRelease(this);
}
}

View File

@@ -5,4 +5,7 @@
#include <Nazara/Utility/AbstractBuffer.hpp>
#include <Nazara/Utility/Debug.hpp>
NzAbstractBuffer::~NzAbstractBuffer() = default;
namespace Nz
{
AbstractBuffer::~AbstractBuffer() = default;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,4 +5,7 @@
#include <Nazara/Utility/FontData.hpp>
#include <Nazara/Utility/Debug.hpp>
NzFontData::~NzFontData() = default;
namespace Nz
{
FontData::~FontData() = default;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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