Merge remote-tracking branch 'origin/master' into Resource-Update
Conflicts: include/Nazara/Audio/Music.hpp include/Nazara/Audio/SoundBuffer.hpp include/Nazara/Core/Resource.hpp include/Nazara/Core/ResourceListener.hpp include/Nazara/Graphics/Material.hpp include/Nazara/Renderer/Context.hpp include/Nazara/Renderer/RenderBuffer.hpp include/Nazara/Renderer/Shader.hpp include/Nazara/Renderer/Texture.hpp include/Nazara/Renderer/UberShader.hpp include/Nazara/Utility/Animation.hpp include/Nazara/Utility/Buffer.hpp include/Nazara/Utility/Image.hpp include/Nazara/Utility/IndexBuffer.hpp include/Nazara/Utility/Mesh.hpp include/Nazara/Utility/SkeletalMesh.hpp include/Nazara/Utility/Skeleton.hpp include/Nazara/Utility/StaticMesh.hpp include/Nazara/Utility/SubMesh.hpp include/Nazara/Utility/VertexBuffer.hpp include/Nazara/Utility/VertexDeclaration.hpp src/Nazara/Core/Resource.cpp src/Nazara/Core/ResourceListener.cpp src/Nazara/Graphics/DeferredRenderQueue.cpp src/Nazara/Graphics/ForwardRenderQueue.cpp src/Nazara/Graphics/SkinningManager.cpp src/Nazara/Renderer/RenderTexture.cpp src/Nazara/Renderer/Renderer.cpp src/Nazara/Utility/Mesh.cpp Former-commit-id: 99b5ad26a19fe9c9f8118da7b5920bffe89f60f8
This commit is contained in:
89
src/Nazara/Utility/AbstractAtlas.cpp
Normal file
89
src/Nazara/Utility/AbstractAtlas.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/AbstractAtlas.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzAbstractAtlas::NzAbstractAtlas() :
|
||||
m_listenersLocked(false)
|
||||
{
|
||||
}
|
||||
|
||||
NzAbstractAtlas::~NzAbstractAtlas()
|
||||
{
|
||||
m_listenersLocked = true;
|
||||
for (auto& pair : m_listeners)
|
||||
pair.first->OnAtlasReleased(this, pair.second);
|
||||
}
|
||||
|
||||
void NzAbstractAtlas::AddListener(Listener* listener, void* userdata) const
|
||||
{
|
||||
if (!m_listenersLocked)
|
||||
m_listeners.insert(std::make_pair(listener, userdata));
|
||||
}
|
||||
|
||||
void NzAbstractAtlas::RemoveListener(Listener* listener) const
|
||||
{
|
||||
if (!m_listenersLocked)
|
||||
m_listeners.erase(listener);
|
||||
}
|
||||
|
||||
void NzAbstractAtlas::NotifyCleared()
|
||||
{
|
||||
m_listenersLocked = true;
|
||||
|
||||
auto it = m_listeners.begin();
|
||||
while (it != m_listeners.end())
|
||||
{
|
||||
if (!it->first->OnAtlasCleared(this, it->second))
|
||||
m_listeners.erase(it++);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
m_listenersLocked = false;
|
||||
}
|
||||
|
||||
void NzAbstractAtlas::NotifyLayerChange(NzAbstractImage* oldLayer, NzAbstractImage* newLayer)
|
||||
{
|
||||
m_listenersLocked = true;
|
||||
|
||||
auto it = m_listeners.begin();
|
||||
while (it != m_listeners.end())
|
||||
{
|
||||
if (!it->first->OnAtlasLayerChange(this, oldLayer, newLayer, it->second))
|
||||
m_listeners.erase(it++);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
m_listenersLocked = false;
|
||||
}
|
||||
|
||||
|
||||
NzAbstractAtlas::Listener::~Listener() = default;
|
||||
|
||||
bool NzAbstractAtlas::Listener::OnAtlasCleared(const NzAbstractAtlas* atlas, void* userdata)
|
||||
{
|
||||
NazaraUnused(atlas);
|
||||
NazaraUnused(userdata);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzAbstractAtlas::Listener::OnAtlasLayerChange(const NzAbstractAtlas* atlas, NzAbstractImage* oldLayer, NzAbstractImage* newLayer, void* userdata)
|
||||
{
|
||||
NazaraUnused(atlas);
|
||||
NazaraUnused(oldLayer);
|
||||
NazaraUnused(newLayer);
|
||||
NazaraUnused(userdata);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzAbstractAtlas::Listener::OnAtlasReleased(const NzAbstractAtlas* atlas, void* userdata)
|
||||
{
|
||||
NazaraUnused(atlas);
|
||||
NazaraUnused(userdata);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
24
src/Nazara/Utility/AbstractImage.cpp
Normal file
24
src/Nazara/Utility/AbstractImage.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/AbstractImage.hpp>
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzAbstractImage::~NzAbstractImage() = default;
|
||||
|
||||
nzUInt8 NzAbstractImage::GetBytesPerPixel() const
|
||||
{
|
||||
return NzPixelFormat::GetBytesPerPixel(GetFormat());
|
||||
}
|
||||
|
||||
bool NzAbstractImage::IsCompressed() const
|
||||
{
|
||||
return NzPixelFormat::IsCompressed(GetFormat());
|
||||
}
|
||||
|
||||
bool NzAbstractImage::IsCubemap() const
|
||||
{
|
||||
return GetType() == nzImageType_Cubemap;
|
||||
}
|
||||
9
src/Nazara/Utility/AbstractTextDrawer.cpp
Normal file
9
src/Nazara/Utility/AbstractTextDrawer.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/AbstractTextDrawer.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzAbstractTextDrawer::~NzAbstractTextDrawer() = default;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
*/
|
||||
|
||||
#include <Nazara/Utility/Algorithm.hpp>
|
||||
#include <Nazara/Math/Basic.hpp>
|
||||
#include <Nazara/Math/Algorithm.hpp>
|
||||
#include <Nazara/Utility/IndexIterator.hpp>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
@@ -189,11 +189,9 @@ namespace
|
||||
void MoveTriangleToEnd(int tri)
|
||||
{
|
||||
auto it = std::find(tri_indices.begin(), tri_indices.end(), tri);
|
||||
int t_ind = (it != tri_indices.end()) ? std::distance(tri_indices.begin(), it) : -1;
|
||||
NazaraAssert(it != tri_indices.end(), "Triangle not found");
|
||||
|
||||
NazaraAssert(t_ind >= 0, "Triangle not found");
|
||||
|
||||
tri_indices.erase(tri_indices.begin() + t_ind, tri_indices.begin() + t_ind + 1);
|
||||
tri_indices.erase(it);
|
||||
tri_indices.push_back(tri);
|
||||
}
|
||||
};
|
||||
@@ -546,15 +544,14 @@ namespace
|
||||
TriangleCacheData* t = &m_triangles[tri];
|
||||
|
||||
// calculate vertex scores
|
||||
float sum = 0.0f;
|
||||
float sum = 0.f;
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
{
|
||||
VertexCacheData *v = &m_vertices[t->verts[i]];
|
||||
float sc = v->current_score;
|
||||
if (!v->calculated)
|
||||
{
|
||||
sc = CalculateVertexScore(t->verts[i]);
|
||||
}
|
||||
|
||||
v->current_score = sc;
|
||||
v->calculated = true;
|
||||
sum += sc;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -48,7 +48,7 @@ bool NzAnimation::AddSequence(const NzSequence& sequence)
|
||||
|
||||
if (sequence.frameCount == 0)
|
||||
{
|
||||
NazaraError("Sequence frame count must be over 0");
|
||||
NazaraError("Sequence frame count must be over zero");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/Buffer.hpp>
|
||||
#include <Nazara/Core/CallOnExit.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Utility/AbstractBuffer.hpp>
|
||||
#include <Nazara/Utility/BufferMapper.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
@@ -15,7 +17,7 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
NzAbstractBuffer* SoftwareBufferFunction(NzBuffer* parent, nzBufferType type)
|
||||
NzAbstractBuffer* SoftwareBufferFactory(NzBuffer* parent, nzBufferType type)
|
||||
{
|
||||
return new NzSoftwareBuffer(parent, type);
|
||||
}
|
||||
@@ -28,19 +30,12 @@ m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
NzBuffer::NzBuffer(nzBufferType type, unsigned int size, nzBufferStorage storage, nzBufferUsage usage) :
|
||||
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);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Failed to create buffer");
|
||||
throw std::runtime_error("Constructor failed");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NzBuffer::~NzBuffer()
|
||||
@@ -65,22 +60,21 @@ bool NzBuffer::CopyContent(const NzBuffer& buffer)
|
||||
#endif
|
||||
|
||||
NzBufferMapper<NzBuffer> mapper(buffer, nzBufferAccess_ReadOnly);
|
||||
|
||||
return Fill(mapper.GetPointer(), 0, buffer.GetSize());
|
||||
}
|
||||
|
||||
bool NzBuffer::Create(unsigned int size, nzBufferStorage storage, nzBufferUsage usage)
|
||||
bool NzBuffer::Create(unsigned int size, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
// Notre buffer est-il supporté ?
|
||||
if (!s_bufferFunctions[storage])
|
||||
if (!IsStorageSupported(storage))
|
||||
{
|
||||
NazaraError("Buffer storage not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<NzAbstractBuffer> impl(s_bufferFunctions[storage](this, m_type));
|
||||
std::unique_ptr<NzAbstractBuffer> impl(s_bufferFactories[storage](this, m_type));
|
||||
if (!impl->Create(size, usage))
|
||||
{
|
||||
NazaraError("Failed to create buffer");
|
||||
@@ -137,7 +131,7 @@ unsigned int NzBuffer::GetSize() const
|
||||
return m_size;
|
||||
}
|
||||
|
||||
nzBufferStorage NzBuffer::GetStorage() const
|
||||
nzUInt32 NzBuffer::GetStorage() const
|
||||
{
|
||||
return m_storage;
|
||||
}
|
||||
@@ -154,7 +148,7 @@ nzBufferUsage NzBuffer::GetUsage() const
|
||||
|
||||
bool NzBuffer::IsHardware() const
|
||||
{
|
||||
return m_storage == nzBufferStorage_Hardware;
|
||||
return m_storage & nzDataStorage_Hardware;
|
||||
}
|
||||
|
||||
bool NzBuffer::IsValid() const
|
||||
@@ -206,7 +200,7 @@ void* NzBuffer::Map(nzBufferAccess access, unsigned int offset, unsigned int siz
|
||||
return m_impl->Map(access, offset, (size == 0) ? m_size-offset : size);
|
||||
}
|
||||
|
||||
bool NzBuffer::SetStorage(nzBufferStorage storage)
|
||||
bool NzBuffer::SetStorage(nzUInt32 storage)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
@@ -219,13 +213,11 @@ bool NzBuffer::SetStorage(nzBufferStorage storage)
|
||||
if (m_storage == storage)
|
||||
return true;
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!IsSupported(storage))
|
||||
if (!IsStorageSupported(storage))
|
||||
{
|
||||
NazaraError("Storage not supported");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void* ptr = m_impl->Map(nzBufferAccess_ReadOnly, 0, m_size);
|
||||
if (!ptr)
|
||||
@@ -234,31 +226,36 @@ bool NzBuffer::SetStorage(nzBufferStorage storage)
|
||||
return false;
|
||||
}
|
||||
|
||||
NzAbstractBuffer* impl = s_bufferFunctions[storage](this, m_type);
|
||||
NzCallOnExit unmapMyImpl([this]()
|
||||
{
|
||||
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");
|
||||
delete impl;
|
||||
m_impl->Unmap();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NzCallOnExit destroyImpl([&impl]()
|
||||
{
|
||||
impl->Destroy();
|
||||
});
|
||||
|
||||
if (!impl->Fill(ptr, 0, m_size))
|
||||
{
|
||||
NazaraError("Failed to fill buffer");
|
||||
impl->Destroy();
|
||||
delete impl;
|
||||
m_impl->Unmap();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_impl->Unmap();
|
||||
destroyImpl.Reset();
|
||||
|
||||
unmapMyImpl.CallAndReset();
|
||||
m_impl->Destroy();
|
||||
delete m_impl;
|
||||
|
||||
m_impl = impl;
|
||||
m_impl = impl.release();
|
||||
m_storage = storage;
|
||||
|
||||
return true;
|
||||
@@ -275,29 +272,29 @@ void NzBuffer::Unmap() const
|
||||
#endif
|
||||
|
||||
if (!m_impl->Unmap())
|
||||
NazaraWarning("Failed to unmap buffer (it's content is undefined)"); ///TODO: Unexpected ?
|
||||
NazaraWarning("Failed to unmap buffer (it's content may be undefined)"); ///TODO: Unexpected ?
|
||||
}
|
||||
|
||||
bool NzBuffer::IsSupported(nzBufferStorage storage)
|
||||
bool NzBuffer::IsStorageSupported(nzUInt32 storage)
|
||||
{
|
||||
return s_bufferFunctions[storage] != nullptr;
|
||||
return s_bufferFactories[storage] != nullptr;
|
||||
}
|
||||
|
||||
void NzBuffer::SetBufferFunction(nzBufferStorage storage, BufferFunction func)
|
||||
void NzBuffer::SetBufferFactory(nzUInt32 storage, BufferFactory func)
|
||||
{
|
||||
s_bufferFunctions[storage] = func;
|
||||
s_bufferFactories[storage] = func;
|
||||
}
|
||||
|
||||
bool NzBuffer::Initialize()
|
||||
{
|
||||
s_bufferFunctions[nzBufferStorage_Software] = SoftwareBufferFunction;
|
||||
s_bufferFactories[nzDataStorage_Software] = SoftwareBufferFactory;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzBuffer::Uninitialize()
|
||||
{
|
||||
std::memset(s_bufferFunctions, 0, (nzBufferStorage_Max+1)*sizeof(NzBuffer::BufferFunction));
|
||||
std::memset(s_bufferFactories, 0, (nzDataStorage_Max+1)*sizeof(NzBuffer::BufferFactory));
|
||||
}
|
||||
|
||||
NzBuffer::BufferFunction NzBuffer::s_bufferFunctions[nzBufferStorage_Max+1] = {0};
|
||||
NzBuffer::BufferFactory NzBuffer::s_bufferFactories[nzDataStorage_Max+1] = {nullptr};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
588
src/Nazara/Utility/Font.cpp
Normal file
588
src/Nazara/Utility/Font.cpp
Normal file
@@ -0,0 +1,588 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/Font.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/FontData.hpp>
|
||||
#include <Nazara/Utility/FontGlyph.hpp>
|
||||
#include <Nazara/Utility/GuillotineImageAtlas.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
const nzUInt8 r_cabinRegular[] = {
|
||||
#include <Nazara/Utility/Resources/Fonts/Cabin-Regular.ttf.h>
|
||||
};
|
||||
}
|
||||
|
||||
bool NzFontParams::IsValid() const
|
||||
{
|
||||
return true; // Rien à tester
|
||||
}
|
||||
|
||||
NzFont::NzFont() :
|
||||
m_atlas(s_defaultAtlas),
|
||||
m_glyphBorder(s_defaultGlyphBorder),
|
||||
m_minimumStepSize(s_defaultMinimumStepSize)
|
||||
{
|
||||
}
|
||||
|
||||
NzFont::~NzFont()
|
||||
{
|
||||
Destroy();
|
||||
SetAtlas(nullptr); // On libère l'atlas proprement
|
||||
}
|
||||
|
||||
void NzFont::ClearGlyphCache()
|
||||
{
|
||||
if (m_atlas)
|
||||
{
|
||||
if (m_atlas.unique())
|
||||
m_atlas->Clear(); // Appellera OnAtlasCleared
|
||||
else
|
||||
{
|
||||
// Au moins une autre police utilise cet atlas, on vire nos glyphes un par un
|
||||
for (auto mapIt = m_glyphes.begin(); mapIt != m_glyphes.end(); ++mapIt)
|
||||
{
|
||||
GlyphMap& glyphMap = mapIt->second;
|
||||
for (auto glyphIt = glyphMap.begin(); glyphIt != glyphMap.end(); ++glyphIt)
|
||||
{
|
||||
Glyph& glyph = glyphIt->second;
|
||||
m_atlas->Free(&glyph.atlasRect, &glyph.layerIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Destruction des glyphes mémorisés et notification
|
||||
m_glyphes.clear();
|
||||
NotifyModified(ModificationCode_GlyphCacheCleared);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzFont::ClearKerningCache()
|
||||
{
|
||||
m_kerningCache.clear();
|
||||
NotifyModified(ModificationCode_KerningCacheCleared);
|
||||
}
|
||||
|
||||
void NzFont::ClearSizeInfoCache()
|
||||
{
|
||||
m_sizeInfoCache.clear();
|
||||
NotifyModified(ModificationCode_SizeInfoCacheCleared);
|
||||
}
|
||||
|
||||
bool NzFont::Create(NzFontData* data)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!data)
|
||||
{
|
||||
NazaraError("Invalid font data");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_data.reset(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzFont::Destroy()
|
||||
{
|
||||
ClearGlyphCache();
|
||||
|
||||
m_data.reset();
|
||||
m_kerningCache.clear();
|
||||
m_sizeInfoCache.clear();
|
||||
}
|
||||
|
||||
bool NzFont::ExtractGlyph(unsigned int characterSize, char32_t character, nzUInt32 style, NzFontGlyph* glyph) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!IsValid())
|
||||
{
|
||||
NazaraError("Invalid font");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_data->ExtractGlyph(characterSize, character, style, glyph);
|
||||
}
|
||||
|
||||
const std::shared_ptr<NzAbstractAtlas>& NzFont::GetAtlas() const
|
||||
{
|
||||
return m_atlas;
|
||||
}
|
||||
|
||||
unsigned int NzFont::GetCachedGlyphCount(unsigned int characterSize, nzUInt32 style) const
|
||||
{
|
||||
nzUInt64 key = ComputeKey(characterSize, style);
|
||||
auto it = m_glyphes.find(key);
|
||||
if (it == m_glyphes.end())
|
||||
return 0;
|
||||
|
||||
return it->second.size();
|
||||
}
|
||||
|
||||
unsigned int NzFont::GetCachedGlyphCount() const
|
||||
{
|
||||
unsigned int count = 0;
|
||||
for (auto& pair : m_glyphes)
|
||||
count += pair.second.size();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
NzString NzFont::GetFamilyName() const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!IsValid())
|
||||
{
|
||||
NazaraError("Invalid font");
|
||||
return NzString("Invalid font");
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_data->GetFamilyName();
|
||||
}
|
||||
|
||||
int NzFont::GetKerning(unsigned int characterSize, char32_t first, char32_t second) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!IsValid())
|
||||
{
|
||||
NazaraError("Invalid font");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// On utilise un cache car la méthode interne QueryKerning peut se révéler coûteuse (car pouvant induire un changement de taille)
|
||||
auto& map = m_kerningCache[characterSize];
|
||||
|
||||
nzUInt64 key = (static_cast<nzUInt64>(first) << 32) | second; // Combinaison de deux caractères 32 bits dans un nombre 64 bits
|
||||
|
||||
auto it = map.find(key);
|
||||
if (it == map.end())
|
||||
{
|
||||
// Absent du cache: on va demander l'information à la police
|
||||
int kerning = m_data->QueryKerning(characterSize, first, second);
|
||||
map.insert(std::make_pair(key, kerning));
|
||||
|
||||
return kerning;
|
||||
}
|
||||
else
|
||||
return it->second; // Présent dans le cache, tout va bien
|
||||
}
|
||||
|
||||
const NzFont::Glyph& NzFont::GetGlyph(unsigned int characterSize, nzUInt32 style, char32_t character) const
|
||||
{
|
||||
nzUInt64 key = ComputeKey(characterSize, style);
|
||||
return PrecacheGlyph(m_glyphes[key], characterSize, style, character);
|
||||
}
|
||||
|
||||
unsigned int NzFont::GetGlyphBorder() const
|
||||
{
|
||||
return m_glyphBorder;
|
||||
}
|
||||
|
||||
unsigned int NzFont::GetMinimumStepSize() const
|
||||
{
|
||||
return m_minimumStepSize;
|
||||
}
|
||||
|
||||
const NzFont::SizeInfo& NzFont::GetSizeInfo(unsigned int characterSize) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!IsValid())
|
||||
{
|
||||
NazaraError("Invalid font");
|
||||
|
||||
static SizeInfo dummy;
|
||||
return dummy;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto it = m_sizeInfoCache.find(characterSize);
|
||||
if (it == m_sizeInfoCache.end())
|
||||
{
|
||||
SizeInfo sizeInfo;
|
||||
sizeInfo.lineHeight = m_data->QueryLineHeight(characterSize);
|
||||
sizeInfo.underlinePosition = m_data->QueryUnderlinePosition(characterSize);
|
||||
sizeInfo.underlineThickness = m_data->QueryUnderlineThickness(characterSize);
|
||||
|
||||
NzFontGlyph glyph;
|
||||
if (m_data->ExtractGlyph(characterSize, ' ', nzTextStyle_Regular, &glyph))
|
||||
sizeInfo.spaceAdvance = glyph.advance;
|
||||
else
|
||||
{
|
||||
NazaraWarning("Failed to extract space character from font, using half the size");
|
||||
sizeInfo.spaceAdvance = characterSize/2;
|
||||
}
|
||||
|
||||
it = m_sizeInfoCache.insert(std::make_pair(characterSize, sizeInfo)).first;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
NzString NzFont::GetStyleName() const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!IsValid())
|
||||
{
|
||||
NazaraError("Invalid font");
|
||||
return NzString("Invalid font");
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_data->GetStyleName();
|
||||
}
|
||||
|
||||
bool NzFont::IsValid() const
|
||||
{
|
||||
return m_data != nullptr;
|
||||
}
|
||||
|
||||
bool NzFont::Precache(unsigned int characterSize, nzUInt32 style, char32_t character) const
|
||||
{
|
||||
nzUInt64 key = ComputeKey(characterSize, style);
|
||||
return PrecacheGlyph(m_glyphes[key], characterSize, style, character).valid;
|
||||
}
|
||||
|
||||
bool NzFont::Precache(unsigned int characterSize, nzUInt32 style, const NzString& characterSet) const
|
||||
{
|
||||
unsigned int size;
|
||||
std::unique_ptr<char32_t[]> characters(characterSet.GetUtf32Buffer(&size));
|
||||
if (!characters)
|
||||
{
|
||||
NazaraError("Invalid character set");
|
||||
return false;
|
||||
}
|
||||
|
||||
nzUInt64 key = ComputeKey(characterSize, style);
|
||||
auto& glyphMap = m_glyphes[key];
|
||||
for (unsigned int i = 0; i < size; ++i)
|
||||
PrecacheGlyph(glyphMap, characterSize, style, characters[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzFont::OpenFromFile(const NzString& filePath, const NzFontParams& params)
|
||||
{
|
||||
return NzFontLoader::LoadFromFile(this, filePath, params);
|
||||
}
|
||||
|
||||
bool NzFont::OpenFromMemory(const void* data, std::size_t size, const NzFontParams& params)
|
||||
{
|
||||
return NzFontLoader::LoadFromMemory(this, data, size, params);
|
||||
}
|
||||
|
||||
bool NzFont::OpenFromStream(NzInputStream& stream, const NzFontParams& params)
|
||||
{
|
||||
return NzFontLoader::LoadFromStream(this, stream, params);
|
||||
}
|
||||
|
||||
void NzFont::SetAtlas(const std::shared_ptr<NzAbstractAtlas>& atlas)
|
||||
{
|
||||
if (m_atlas != atlas)
|
||||
{
|
||||
ClearGlyphCache();
|
||||
|
||||
if (m_atlas)
|
||||
m_atlas->RemoveListener(this);
|
||||
|
||||
m_atlas = atlas;
|
||||
if (m_atlas)
|
||||
m_atlas->AddListener(this);
|
||||
|
||||
NotifyModified(ModificationCode_AtlasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void NzFont::SetGlyphBorder(unsigned int borderSize)
|
||||
{
|
||||
if (m_glyphBorder != borderSize)
|
||||
{
|
||||
m_glyphBorder = borderSize;
|
||||
ClearGlyphCache();
|
||||
}
|
||||
}
|
||||
|
||||
void NzFont::SetMinimumStepSize(unsigned int minimumStepSize)
|
||||
{
|
||||
if (m_minimumStepSize != minimumStepSize)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (minimumStepSize == 0)
|
||||
{
|
||||
NazaraError("Minimum step size cannot be zero as it implies division by zero");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_minimumStepSize = minimumStepSize;
|
||||
ClearGlyphCache();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<NzAbstractAtlas> NzFont::GetDefaultAtlas()
|
||||
{
|
||||
return s_defaultAtlas;
|
||||
}
|
||||
|
||||
NzFont* NzFont::GetDefault()
|
||||
{
|
||||
// Nous n'initialisons la police par défaut qu'à la demande pour qu'elle prenne
|
||||
// les paramètres par défaut (qui peuvent avoir étés changés par l'utilisateur),
|
||||
// et pour ne pas consommer de la mémoire vive inutilement (si elle n'est jamais utilisée, elle n'est jamais ouverte).
|
||||
|
||||
if (!s_defaultFont)
|
||||
{
|
||||
std::unique_ptr<NzFont> cabin(new NzFont);
|
||||
cabin->SetPersistent(true);
|
||||
|
||||
if (!cabin->OpenFromMemory(r_cabinRegular, sizeof(r_cabinRegular)))
|
||||
{
|
||||
NazaraError("Failed to open default font");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s_defaultFont = cabin.release();
|
||||
}
|
||||
|
||||
return s_defaultFont;
|
||||
}
|
||||
|
||||
unsigned int NzFont::GetDefaultGlyphBorder()
|
||||
{
|
||||
return s_defaultGlyphBorder;
|
||||
}
|
||||
|
||||
unsigned int NzFont::GetDefaultMinimumStepSize()
|
||||
{
|
||||
return s_defaultMinimumStepSize;
|
||||
}
|
||||
|
||||
bool NzFont::Initialize()
|
||||
{
|
||||
s_defaultAtlas.reset(new NzGuillotineImageAtlas);
|
||||
s_defaultGlyphBorder = 1;
|
||||
s_defaultMinimumStepSize = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzFont::SetDefaultAtlas(const std::shared_ptr<NzAbstractAtlas>& atlas)
|
||||
{
|
||||
s_defaultAtlas = atlas;
|
||||
}
|
||||
|
||||
void NzFont::SetDefaultGlyphBorder(unsigned int borderSize)
|
||||
{
|
||||
s_defaultGlyphBorder = borderSize;
|
||||
}
|
||||
|
||||
void NzFont::SetDefaultMinimumStepSize(unsigned int minimumStepSize)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (minimumStepSize == 0)
|
||||
{
|
||||
NazaraError("Minimum step size cannot be zero as it implies division by zero");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
s_defaultMinimumStepSize = minimumStepSize;
|
||||
}
|
||||
|
||||
void NzFont::Uninitialize()
|
||||
{
|
||||
s_defaultAtlas.reset();
|
||||
|
||||
// On rend la police non-persistente et on demande la vérification du compteur (pouvant entraîner la libération de la ressource)
|
||||
if (s_defaultFont)
|
||||
{
|
||||
s_defaultFont->SetPersistent(false, true);
|
||||
s_defaultFont = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nzUInt64 NzFont::ComputeKey(unsigned int characterSize, nzUInt32 style) const
|
||||
{
|
||||
// On prend le pas en compte
|
||||
nzUInt64 sizePart = static_cast<nzUInt32>((characterSize/m_minimumStepSize)*m_minimumStepSize);
|
||||
|
||||
// Ainsi que le style (uniquement le gras et l'italique, les autres sont gérés par un TextDrawer)
|
||||
nzUInt64 stylePart = 0;
|
||||
|
||||
if (style & nzTextStyle_Bold)
|
||||
stylePart |= nzTextStyle_Bold;
|
||||
|
||||
if (style & nzTextStyle_Italic)
|
||||
stylePart |= nzTextStyle_Italic;
|
||||
|
||||
return (stylePart << 32) | sizePart;
|
||||
}
|
||||
|
||||
bool NzFont::OnAtlasCleared(const NzAbstractAtlas* atlas, void* userdata)
|
||||
{
|
||||
NazaraUnused(atlas);
|
||||
NazaraUnused(userdata);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
// Est-ce qu'il s'agit bien de notre atlas ?
|
||||
if (m_atlas.get() != atlas)
|
||||
{
|
||||
NazaraInternalError("Notified by a non-listening-to resource");
|
||||
return false; // On ne veut plus être notifié par cette ressource, évidemment
|
||||
}
|
||||
#endif
|
||||
|
||||
// Notre atlas vient d'être vidé, détruisons le cache de glyphe
|
||||
m_glyphes.clear();
|
||||
NotifyModified(ModificationCode_GlyphCacheCleared);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzFont::OnAtlasLayerChange(const NzAbstractAtlas* atlas, NzAbstractImage* oldLayer, NzAbstractImage* newLayer, void* userdata)
|
||||
{
|
||||
NazaraUnused(atlas);
|
||||
NazaraUnused(oldLayer);
|
||||
NazaraUnused(newLayer);
|
||||
NazaraUnused(userdata);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
// Est-ce qu'il s'agit bien de notre atlas ?
|
||||
if (m_atlas.get() != atlas)
|
||||
{
|
||||
NazaraInternalError("Notified by a non-listening-to resource");
|
||||
return false; // On ne veut plus être notifié par cette ressource, évidemment
|
||||
}
|
||||
#endif
|
||||
|
||||
// Pour faciliter le travail des ressources qui nous écoutent
|
||||
NotifyModified(ModificationCode_AtlasLayerChanged);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzFont::OnAtlasReleased(const NzAbstractAtlas* atlas, void* userdata)
|
||||
{
|
||||
NazaraUnused(atlas);
|
||||
NazaraUnused(userdata);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
// Est-ce qu'il s'agit bien de notre atlas ?
|
||||
if (m_atlas.get() != atlas)
|
||||
{
|
||||
NazaraInternalError("Notified by a non-listening-to resource");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Nous ne pouvons pas faire grand chose d'autre que de balancer une erreur à la tête de l'utilisateur avant un potentiel crash...
|
||||
NazaraError("Atlas has been released while in use");
|
||||
}
|
||||
|
||||
const NzFont::Glyph& NzFont::PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, nzUInt32 style, char32_t character) const
|
||||
{
|
||||
auto it = glyphMap.find(character);
|
||||
if (it != glyphMap.end()) // Si le glyphe n'est pas déjà chargé
|
||||
return it->second;
|
||||
|
||||
Glyph& glyph = glyphMap[character]; // Insertion du glyphe
|
||||
glyph.valid = false;
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_atlas)
|
||||
{
|
||||
NazaraError("Font has no atlas");
|
||||
return glyph;
|
||||
}
|
||||
#endif
|
||||
|
||||
// On vérifie que le style demandé est supporté par la police (dans le cas contraire il devra être simulé au rendu)
|
||||
glyph.requireFauxBold = false;
|
||||
glyph.requireFauxItalic = false;
|
||||
|
||||
nzUInt32 supportedStyle = style;
|
||||
if (style & nzTextStyle_Bold && !m_data->SupportsStyle(nzTextStyle_Bold))
|
||||
{
|
||||
glyph.requireFauxBold = true;
|
||||
supportedStyle &= ~nzTextStyle_Bold;
|
||||
}
|
||||
|
||||
if (style & nzTextStyle_Italic && !m_data->SupportsStyle(nzTextStyle_Italic))
|
||||
{
|
||||
glyph.requireFauxItalic = true;
|
||||
supportedStyle &= ~nzTextStyle_Italic;
|
||||
}
|
||||
|
||||
// Est-ce que la police supporte le style demandé ?
|
||||
if (style == supportedStyle)
|
||||
{
|
||||
// On extrait le glyphe depuis la police
|
||||
NzFontGlyph fontGlyph;
|
||||
if (ExtractGlyph(characterSize, character, style, &fontGlyph))
|
||||
{
|
||||
glyph.atlasRect.width = fontGlyph.image.GetWidth();
|
||||
glyph.atlasRect.height = fontGlyph.image.GetHeight();
|
||||
|
||||
// Insertion du rectangle dans l'un des atlas
|
||||
if (glyph.atlasRect.width > 0 && glyph.atlasRect.height > 0) // Si l'image contient quelque chose
|
||||
{
|
||||
// Bordure (pour éviter le débordement lors du filtrage)
|
||||
glyph.atlasRect.width += m_glyphBorder*2;
|
||||
glyph.atlasRect.height += m_glyphBorder*2;
|
||||
|
||||
// Insertion du rectangle dans l'atlas virtuel
|
||||
if (!m_atlas->Insert(fontGlyph.image, &glyph.atlasRect, &glyph.flipped, &glyph.layerIndex))
|
||||
{
|
||||
NazaraError("Failed to insert glyph into atlas");
|
||||
return glyph;
|
||||
}
|
||||
|
||||
// Compensation de la bordure (centrage du glyphe)
|
||||
glyph.atlasRect.x += m_glyphBorder;
|
||||
glyph.atlasRect.y += m_glyphBorder;
|
||||
glyph.atlasRect.width -= m_glyphBorder*2;
|
||||
glyph.atlasRect.height -= m_glyphBorder*2;
|
||||
}
|
||||
|
||||
glyph.aabb = fontGlyph.aabb;
|
||||
glyph.advance = fontGlyph.advance;
|
||||
glyph.valid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
NazaraWarning("Failed to extract glyph \"" + NzString::Unicode(character) + "\"");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// La police ne supporte pas le style demandé, nous allons donc précharger le glyphe supportant le style "minimum" supporté
|
||||
// et copier ses données
|
||||
nzUInt64 newKey = ComputeKey(characterSize, supportedStyle);
|
||||
const Glyph& referenceGlyph = PrecacheGlyph(m_glyphes[newKey], characterSize, supportedStyle, character);
|
||||
if (referenceGlyph.valid)
|
||||
{
|
||||
glyph.aabb = referenceGlyph.aabb;
|
||||
glyph.advance = referenceGlyph.advance;
|
||||
glyph.atlasRect = referenceGlyph.atlasRect;
|
||||
glyph.flipped = referenceGlyph.flipped;
|
||||
glyph.layerIndex = referenceGlyph.layerIndex;
|
||||
glyph.valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
return glyph;
|
||||
}
|
||||
|
||||
std::shared_ptr<NzAbstractAtlas> NzFont::s_defaultAtlas;
|
||||
NzFont* NzFont::s_defaultFont;
|
||||
NzFontLoader::LoaderList NzFont::s_loaders;
|
||||
unsigned int NzFont::s_defaultGlyphBorder;
|
||||
unsigned int NzFont::s_defaultMinimumStepSize;
|
||||
8
src/Nazara/Utility/FontData.cpp
Normal file
8
src/Nazara/Utility/FontData.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/FontData.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzFontData::~NzFontData() = default;
|
||||
264
src/Nazara/Utility/GuillotineImageAtlas.cpp
Normal file
264
src/Nazara/Utility/GuillotineImageAtlas.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/GuillotineImageAtlas.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
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();
|
||||
NotifyCleared();
|
||||
}
|
||||
|
||||
void NzGuillotineImageAtlas::Free(NzSparsePtr<const NzRectui> rects, NzSparsePtr<unsigned int> layers, unsigned int count)
|
||||
{
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (layers[i] >= m_layers.size())
|
||||
{
|
||||
NazaraWarning("Rectangle #" + NzString::Number(i) + " belong to an out-of-bounds layer (" + NzString::Number(i) + " >= " + NzString::Number(m_layers.size()) + ")");
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_layers[layers[i]].binPack.FreeRectangle(rects[i]);
|
||||
m_layers[layers[i]].freedRectangles++;
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
NazaraError("Layer index out of range (" + NzString::Number(layerIndex) + " >= " + NzString::Number(m_layers.size()) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
#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)
|
||||
{
|
||||
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 ?
|
||||
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
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
NotifyLayerChange(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)
|
||||
{
|
||||
unsigned int glyphWidth = glyph.image.GetWidth();
|
||||
unsigned int glyphHeight = glyph.image.GetHeight();
|
||||
|
||||
// Calcul de l'éventuel padding (pixels de contour)
|
||||
unsigned int paddingX;
|
||||
unsigned int paddingY;
|
||||
if (glyph.flipped)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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, 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();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/Image.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
///TODO: Rajouter des warnings (Formats compressés avec les méthodes Copy/Update, tests taille dans Copy)
|
||||
///TODO: Rendre les méthodes exception-safe
|
||||
///TODO: Rendre les méthodes exception-safe (Virer toute cette merde de calcul de pointeurs, faire usage du RAII)
|
||||
///FIXME: Gérer correctement les formats utilisant moins d'un octet par pixel
|
||||
|
||||
namespace
|
||||
{
|
||||
inline unsigned int GetLevelSize(unsigned int size, nzUInt8 level)
|
||||
{
|
||||
if (size == 0) // Possible dans le cas d'une image invalide
|
||||
return 0;
|
||||
|
||||
return std::max(size >> level, 1U);
|
||||
}
|
||||
|
||||
@@ -28,7 +34,7 @@ namespace
|
||||
|
||||
bool NzImageParams::IsValid() const
|
||||
{
|
||||
return true;
|
||||
return true; // Rien à vérifier
|
||||
}
|
||||
|
||||
NzImage::NzImage() :
|
||||
@@ -39,15 +45,8 @@ m_sharedImage(&emptyImage)
|
||||
NzImage::NzImage(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount) :
|
||||
m_sharedImage(&emptyImage)
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowException);
|
||||
Create(type, format, width, height, depth, levelCount);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!m_sharedImage)
|
||||
{
|
||||
NazaraError("Failed to create image");
|
||||
throw std::runtime_error("Constructor failed");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NzImage::NzImage(const NzImage& image) :
|
||||
@@ -59,12 +58,6 @@ m_sharedImage(image.m_sharedImage)
|
||||
m_sharedImage->refCount++;
|
||||
}
|
||||
|
||||
NzImage::NzImage(NzImage&& image) noexcept :
|
||||
m_sharedImage(image.m_sharedImage)
|
||||
{
|
||||
image.m_sharedImage = &emptyImage;
|
||||
}
|
||||
|
||||
NzImage::NzImage(SharedImage* sharedImage) :
|
||||
m_sharedImage(sharedImage)
|
||||
{
|
||||
@@ -196,7 +189,7 @@ void NzImage::Copy(const NzImage& source, const NzBoxui& srcBox, const NzVector3
|
||||
|
||||
bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount)
|
||||
{
|
||||
ReleaseImage();
|
||||
Destroy();
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!NzPixelFormat::IsValid(format))
|
||||
@@ -228,13 +221,13 @@ bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width,
|
||||
case nzImageType_1D:
|
||||
if (height > 1)
|
||||
{
|
||||
NazaraError("1D textures must be 1 height");
|
||||
NazaraError("1D textures must be 1 tall");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (depth > 1)
|
||||
{
|
||||
NazaraError("1D textures must be 1 depth");
|
||||
NazaraError("1D textures must be 1 deep");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -243,7 +236,7 @@ bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width,
|
||||
case nzImageType_2D:
|
||||
if (depth > 1)
|
||||
{
|
||||
NazaraError("2D textures must be 1 depth");
|
||||
NazaraError("2D textures must be 1 deep");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -255,7 +248,7 @@ bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width,
|
||||
case nzImageType_Cubemap:
|
||||
if (depth > 1)
|
||||
{
|
||||
NazaraError("Cubemaps must be 1 depth");
|
||||
NazaraError("Cubemaps must be 1 deep");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -272,7 +265,7 @@ bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width,
|
||||
}
|
||||
#endif
|
||||
|
||||
levelCount = std::min(levelCount, GetMaxLevel(width, height, depth));
|
||||
levelCount = std::min(levelCount, GetMaxLevel(type, width, height, depth));
|
||||
|
||||
nzUInt8** levels = new nzUInt8*[levelCount];
|
||||
|
||||
@@ -465,7 +458,7 @@ bool NzImage::Fill(const NzColor& color, const NzRectui& rect, unsigned int z)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rect.x+rect.width > m_sharedImage->width || rect.y+rect.height > m_sharedImage->height)
|
||||
if (rect.x + rect.width > m_sharedImage->width || rect.y + rect.height > m_sharedImage->height)
|
||||
{
|
||||
NazaraError("Rectangle dimensions are out of bounds");
|
||||
return false;
|
||||
@@ -474,7 +467,7 @@ bool NzImage::Fill(const NzColor& color, const NzRectui& rect, unsigned int z)
|
||||
unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth;
|
||||
if (z >= depth)
|
||||
{
|
||||
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')');
|
||||
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= " + NzString::Number(depth) + ')');
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -587,11 +580,6 @@ bool NzImage::FlipVertically()
|
||||
return true;
|
||||
}
|
||||
|
||||
nzUInt8 NzImage::GetBytesPerPixel() const
|
||||
{
|
||||
return NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
|
||||
}
|
||||
|
||||
const nzUInt8* NzImage::GetConstPixels(unsigned int x, unsigned int y, unsigned int z, nzUInt8 level) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
@@ -674,7 +662,50 @@ nzUInt8 NzImage::GetLevelCount() const
|
||||
|
||||
nzUInt8 NzImage::GetMaxLevel() const
|
||||
{
|
||||
return GetMaxLevel(m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth);
|
||||
return GetMaxLevel(m_sharedImage->type, m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth);
|
||||
}
|
||||
|
||||
unsigned int NzImage::GetMemoryUsage() const
|
||||
{
|
||||
unsigned int width = m_sharedImage->width;
|
||||
unsigned int height = m_sharedImage->height;
|
||||
unsigned int depth = m_sharedImage->depth;
|
||||
|
||||
unsigned int size = 0;
|
||||
for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i)
|
||||
{
|
||||
size += width * height * depth;
|
||||
|
||||
if (width > 1)
|
||||
width >>= 1;
|
||||
|
||||
if (height > 1)
|
||||
height >>= 1;
|
||||
|
||||
if (depth > 1)
|
||||
depth >>= 1;
|
||||
}
|
||||
|
||||
if (m_sharedImage->type == nzImageType_Cubemap)
|
||||
size *= 6;
|
||||
|
||||
return size * NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
|
||||
}
|
||||
|
||||
unsigned int NzImage::GetMemoryUsage(nzUInt8 level) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (level >= m_sharedImage->levelCount)
|
||||
{
|
||||
NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')');
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return (GetLevelSize(m_sharedImage->width, level)) *
|
||||
(GetLevelSize(m_sharedImage->height, level)) *
|
||||
((m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level)) *
|
||||
NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
|
||||
}
|
||||
|
||||
NzColor NzImage::GetPixelColor(unsigned int x, unsigned int y, unsigned int z) const
|
||||
@@ -694,20 +725,20 @@ NzColor NzImage::GetPixelColor(unsigned int x, unsigned int y, unsigned int z) c
|
||||
|
||||
if (x >= m_sharedImage->width)
|
||||
{
|
||||
NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')');
|
||||
NazaraError("X value exceeds width (" + NzString::Number(x) + " >= " + NzString::Number(m_sharedImage->width) + ')');
|
||||
return NzColor();
|
||||
}
|
||||
|
||||
if (y >= m_sharedImage->height)
|
||||
{
|
||||
NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')');
|
||||
NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= " + NzString::Number(m_sharedImage->height) + ')');
|
||||
return NzColor();
|
||||
}
|
||||
|
||||
unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth;
|
||||
if (z >= depth)
|
||||
{
|
||||
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')');
|
||||
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= " + NzString::Number(depth) + ')');
|
||||
return NzColor();
|
||||
}
|
||||
#endif
|
||||
@@ -741,7 +772,7 @@ nzUInt8* NzImage::GetPixels(unsigned int x, unsigned int y, unsigned int z, nzUI
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (x >= width)
|
||||
{
|
||||
NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(width) + ')');
|
||||
NazaraError("X value exceeds width (" + NzString::Number(x) + " >= " + NzString::Number(width) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
@@ -750,14 +781,14 @@ nzUInt8* NzImage::GetPixels(unsigned int x, unsigned int y, unsigned int z, nzUI
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (y >= height)
|
||||
{
|
||||
NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= (" + NzString::Number(height) + ')');
|
||||
NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= " + NzString::Number(height) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level);
|
||||
if (z >= depth)
|
||||
{
|
||||
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')');
|
||||
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= " + NzString::Number(depth) + ')');
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -773,47 +804,17 @@ nzUInt8* NzImage::GetPixels(unsigned int x, unsigned int y, unsigned int z, nzUI
|
||||
return GetPixelPtr(m_sharedImage->pixels[level], NzPixelFormat::GetBytesPerPixel(m_sharedImage->format), x, y, z, width, height);
|
||||
}
|
||||
|
||||
unsigned int NzImage::GetSize() const
|
||||
{
|
||||
unsigned int width = m_sharedImage->width;
|
||||
unsigned int height = m_sharedImage->height;
|
||||
unsigned int depth = m_sharedImage->depth;
|
||||
|
||||
unsigned int size = 0;
|
||||
for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i)
|
||||
{
|
||||
size += width * height * depth;
|
||||
|
||||
if (width > 1)
|
||||
width >>= 1;
|
||||
|
||||
if (height > 1)
|
||||
height >>= 1;
|
||||
|
||||
if (depth > 1)
|
||||
depth >>= 1;
|
||||
}
|
||||
|
||||
if (m_sharedImage->type == nzImageType_Cubemap)
|
||||
size *= 6;
|
||||
|
||||
return size * NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
|
||||
}
|
||||
|
||||
unsigned int NzImage::GetSize(nzUInt8 level) const
|
||||
NzVector3ui NzImage::GetSize(nzUInt8 level) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (level >= m_sharedImage->levelCount)
|
||||
{
|
||||
NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')');
|
||||
return 0;
|
||||
return NzVector3ui::Zero();
|
||||
}
|
||||
#endif
|
||||
|
||||
return (GetLevelSize(m_sharedImage->width, level)) *
|
||||
(GetLevelSize(m_sharedImage->height, level)) *
|
||||
((m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level)) *
|
||||
NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
|
||||
return NzVector3ui(GetLevelSize(m_sharedImage->width, level), GetLevelSize(m_sharedImage->height, level), GetLevelSize(m_sharedImage->depth, level));
|
||||
}
|
||||
|
||||
nzImageType NzImage::GetType() const
|
||||
@@ -834,16 +835,6 @@ unsigned int NzImage::GetWidth(nzUInt8 level) const
|
||||
return GetLevelSize(m_sharedImage->width, level);
|
||||
}
|
||||
|
||||
bool NzImage::IsCompressed() const
|
||||
{
|
||||
return NzPixelFormat::IsCompressed(m_sharedImage->format);
|
||||
}
|
||||
|
||||
bool NzImage::IsCubemap() const
|
||||
{
|
||||
return m_sharedImage->type == nzImageType_Cubemap;
|
||||
}
|
||||
|
||||
bool NzImage::IsValid() const
|
||||
{
|
||||
return m_sharedImage != &emptyImage;
|
||||
@@ -864,6 +855,105 @@ bool NzImage::LoadFromStream(NzInputStream& stream, const NzImageParams& params)
|
||||
return NzImageLoader::LoadFromStream(this, stream, params);
|
||||
}
|
||||
|
||||
// LoadArray
|
||||
bool NzImage::LoadArrayFromFile(const NzString& filePath, const NzImageParams& imageParams, const NzVector2ui& atlasSize)
|
||||
{
|
||||
NzImage image;
|
||||
if (!image.LoadFromFile(filePath, imageParams))
|
||||
{
|
||||
NazaraError("Failed to load image");
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadArrayFromImage(image, atlasSize);
|
||||
}
|
||||
|
||||
bool NzImage::LoadArrayFromImage(const NzImage& image, const NzVector2ui& atlasSize)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!image.IsValid())
|
||||
{
|
||||
NazaraError("Image must be valid");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (atlasSize.x == 0)
|
||||
{
|
||||
NazaraError("Atlas width must be over zero");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (atlasSize.y == 0)
|
||||
{
|
||||
NazaraError("Atlas height must be over zero");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
nzImageType type = image.GetType();
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (type != nzImageType_1D && type != nzImageType_2D)
|
||||
{
|
||||
NazaraError("Image type not handled (0x" + NzString::Number(type, 16) + ')');
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
NzVector2ui imageSize(image.GetWidth(), image.GetHeight());
|
||||
|
||||
if (imageSize.x % atlasSize.x != 0)
|
||||
{
|
||||
NazaraWarning("Image width is not divisible by atlas width (" + NzString::Number(imageSize.x) + " mod " + NzString::Number(atlasSize.x) + " != 0)");
|
||||
}
|
||||
|
||||
if (imageSize.y % atlasSize.y != 0)
|
||||
{
|
||||
NazaraWarning("Image height is not divisible by atlas height (" + NzString::Number(imageSize.y) + " mod " + NzString::Number(atlasSize.y) + " != 0)");
|
||||
}
|
||||
|
||||
NzVector2ui faceSize = imageSize/atlasSize;
|
||||
|
||||
unsigned int layerCount = atlasSize.x*atlasSize.y;
|
||||
|
||||
// Selon le type de l'image de base, on va créer un array d'images 2D ou 1D
|
||||
if (type == nzImageType_2D)
|
||||
Create(nzImageType_2D_Array, image.GetFormat(), faceSize.x, faceSize.y, layerCount);
|
||||
else
|
||||
Create(nzImageType_1D_Array, image.GetFormat(), faceSize.x, layerCount);
|
||||
|
||||
unsigned int layer = 0;
|
||||
for (unsigned int j = 0; j < atlasSize.y; ++j)
|
||||
for (unsigned int i = 0; i < atlasSize.x; ++i)
|
||||
Copy(image, NzRectui(i*faceSize.x, j*faceSize.y, faceSize.x, faceSize.y), NzVector3ui(0, 0, layer++));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzImage::LoadArrayFromMemory(const void* data, std::size_t size, const NzImageParams& imageParams, const NzVector2ui& atlasSize)
|
||||
{
|
||||
NzImage image;
|
||||
if (!image.LoadFromMemory(data, size, imageParams))
|
||||
{
|
||||
NazaraError("Failed to load image");
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadArrayFromImage(image, atlasSize);
|
||||
}
|
||||
|
||||
bool NzImage::LoadArrayFromStream(NzInputStream& stream, const NzImageParams& imageParams, const NzVector2ui& atlasSize)
|
||||
{
|
||||
NzImage image;
|
||||
if (!image.LoadFromStream(stream, imageParams))
|
||||
{
|
||||
NazaraError("Failed to load image");
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadArrayFromImage(image, atlasSize);
|
||||
}
|
||||
|
||||
bool NzImage::LoadCubemapFromFile(const NzString& filePath, const NzImageParams& imageParams, const NzCubemapParams& cubemapParams)
|
||||
{
|
||||
NzImage image;
|
||||
@@ -884,6 +974,13 @@ bool NzImage::LoadCubemapFromImage(const NzImage& image, const NzCubemapParams&
|
||||
NazaraError("Image must be valid");
|
||||
return false;
|
||||
}
|
||||
|
||||
nzImageType type = image.GetType();
|
||||
if (type != nzImageType_2D)
|
||||
{
|
||||
NazaraError("Image type not handled (0x" + NzString::Number(type, 16) + ')');
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int width = image.GetWidth();
|
||||
@@ -1004,7 +1101,7 @@ void NzImage::SetLevelCount(nzUInt8 levelCount)
|
||||
}
|
||||
#endif
|
||||
|
||||
levelCount = std::min(levelCount, GetMaxLevel(m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth));
|
||||
levelCount = std::min(levelCount, GetMaxLevel());
|
||||
|
||||
if (m_sharedImage->levelCount == levelCount)
|
||||
return;
|
||||
@@ -1013,7 +1110,7 @@ void NzImage::SetLevelCount(nzUInt8 levelCount)
|
||||
|
||||
nzUInt8 oldLevelCount = m_sharedImage->levelCount;
|
||||
nzUInt8 maxLevelCount = std::max(levelCount, oldLevelCount);
|
||||
m_sharedImage->levelCount = levelCount; // Pour faire fonctionner GetSize
|
||||
m_sharedImage->levelCount = levelCount; // Pour faire fonctionner GetMemoryUsage
|
||||
|
||||
nzUInt8** pixels = new nzUInt8*[levelCount];
|
||||
for (unsigned int i = 0; i < maxLevelCount; ++i)
|
||||
@@ -1021,7 +1118,7 @@ void NzImage::SetLevelCount(nzUInt8 levelCount)
|
||||
if (i < oldLevelCount)
|
||||
pixels[i] = m_sharedImage->pixels[i];
|
||||
else if (i < levelCount)
|
||||
pixels[i] = new nzUInt8[GetSize(i)];
|
||||
pixels[i] = new nzUInt8[GetMemoryUsage(i)];
|
||||
else
|
||||
delete[] m_sharedImage->pixels[i];
|
||||
}
|
||||
@@ -1048,20 +1145,20 @@ bool NzImage::SetPixelColor(const NzColor& color, unsigned int x, unsigned int y
|
||||
|
||||
if (x >= m_sharedImage->width)
|
||||
{
|
||||
NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')');
|
||||
NazaraError("X value exceeds width (" + NzString::Number(x) + " >= " + NzString::Number(m_sharedImage->width) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (y >= m_sharedImage->height)
|
||||
{
|
||||
NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')');
|
||||
NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= " + NzString::Number(m_sharedImage->height) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth;
|
||||
if (z >= depth)
|
||||
{
|
||||
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')');
|
||||
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= " + NzString::Number(depth) + ')');
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -1077,25 +1174,25 @@ bool NzImage::SetPixelColor(const NzColor& color, unsigned int x, unsigned int y
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzImage::Update(const nzUInt8* pixels, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level)
|
||||
bool NzImage::Update(const nzUInt8* pixels, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_sharedImage == &emptyImage)
|
||||
{
|
||||
NazaraError("Image must be valid");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pixels)
|
||||
{
|
||||
NazaraError("Invalid pixel source");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (level >= m_sharedImage->levelCount)
|
||||
{
|
||||
NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1107,27 +1204,29 @@ void NzImage::Update(const nzUInt8* pixels, unsigned int srcWidth, unsigned int
|
||||
GetLevelSize(m_sharedImage->depth, level),
|
||||
0, 0,
|
||||
srcWidth, srcHeight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzImage::Update(const nzUInt8* pixels, const NzBoxui& box, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level)
|
||||
bool NzImage::Update(const nzUInt8* pixels, const NzBoxui& box, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_sharedImage == &emptyImage)
|
||||
{
|
||||
NazaraError("Image must be valid");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pixels)
|
||||
{
|
||||
NazaraError("Invalid pixel source");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (level >= m_sharedImage->levelCount)
|
||||
{
|
||||
NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1138,14 +1237,15 @@ void NzImage::Update(const nzUInt8* pixels, const NzBoxui& box, unsigned int src
|
||||
if (!box.IsValid())
|
||||
{
|
||||
NazaraError("Invalid box");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nous n'autorisons pas de modifier plus d'une face du cubemap à la fois (Nous prenons donc la profondeur de base)
|
||||
if (box.x+box.width > width || box.y+box.height > height || box.z+box.depth > GetLevelSize(m_sharedImage->depth, level))
|
||||
unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level);
|
||||
if (box.x+box.width > width || box.y+box.height > height || box.z+box.depth > depth ||
|
||||
(m_sharedImage->type == nzImageType_Cubemap && box.depth > 1)) // Nous n'autorisons pas de modifier plus d'une face du cubemap à la fois
|
||||
{
|
||||
NazaraError("Box dimensions are out of bounds");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1158,63 +1258,13 @@ void NzImage::Update(const nzUInt8* pixels, const NzBoxui& box, unsigned int src
|
||||
box.width, box.height, box.depth,
|
||||
width, height,
|
||||
srcWidth, srcHeight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzImage::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level)
|
||||
bool NzImage::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (m_sharedImage == &emptyImage)
|
||||
{
|
||||
NazaraError("Image must be valid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pixels)
|
||||
{
|
||||
NazaraError("Invalid pixel source");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rect.IsValid())
|
||||
{
|
||||
NazaraError("Invalid rectangle");
|
||||
return;
|
||||
}
|
||||
|
||||
if (level >= m_sharedImage->levelCount)
|
||||
{
|
||||
NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')');
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int width = GetLevelSize(m_sharedImage->width, level);
|
||||
unsigned int height = GetLevelSize(m_sharedImage->height, level);
|
||||
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (rect.x+rect.width > width || rect.y+rect.height > height)
|
||||
{
|
||||
NazaraError("Rectangle dimensions are out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level);
|
||||
if (z >= depth)
|
||||
{
|
||||
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= " + NzString::Number(depth) + ')');
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
EnsureOwnership();
|
||||
|
||||
nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
|
||||
nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[level], bpp, rect.x, rect.y, z, width, height);
|
||||
|
||||
Copy(dstPixels, pixels, bpp,
|
||||
rect.width, rect.height, 1,
|
||||
width, height,
|
||||
srcWidth, srcHeight);
|
||||
return Update(pixels, NzBoxui(rect.x, rect.y, z, rect.width, rect.height, 1), srcWidth, srcHeight, level);
|
||||
}
|
||||
|
||||
NzImage& NzImage::operator=(const NzImage& image)
|
||||
@@ -1228,13 +1278,6 @@ NzImage& NzImage::operator=(const NzImage& image)
|
||||
return *this;
|
||||
}
|
||||
|
||||
NzImage& NzImage::operator=(NzImage&& image) noexcept
|
||||
{
|
||||
std::swap(m_sharedImage, image.m_sharedImage);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void NzImage::Copy(nzUInt8* destination, const nzUInt8* source, nzUInt8 bpp, unsigned int width, unsigned int height, unsigned int depth, unsigned int dstWidth, unsigned int dstHeight, unsigned int srcWidth, unsigned int srcHeight)
|
||||
{
|
||||
if (dstWidth == 0)
|
||||
@@ -1288,6 +1331,29 @@ nzUInt8 NzImage::GetMaxLevel(unsigned int width, unsigned int height, unsigned i
|
||||
return std::max(std::max(std::max(widthLevel, heightLevel), depthLevel), 1U);
|
||||
}
|
||||
|
||||
nzUInt8 NzImage::GetMaxLevel(nzImageType type, unsigned int width, unsigned int height, unsigned int depth)
|
||||
{
|
||||
// Pour éviter que la profondeur ne soit comptée dans le calcul des niveaux
|
||||
switch (type)
|
||||
{
|
||||
case nzImageType_1D:
|
||||
case nzImageType_1D_Array:
|
||||
return GetMaxLevel(width, 1U, 1U);
|
||||
|
||||
case nzImageType_2D:
|
||||
case nzImageType_2D_Array:
|
||||
case nzImageType_Cubemap:
|
||||
return GetMaxLevel(width, height, 1U);
|
||||
|
||||
case nzImageType_3D:
|
||||
return GetMaxLevel(width, height, depth);
|
||||
}
|
||||
|
||||
NazaraError("Image type not handled (0x" + NzString::Number(type, 16) + ')');
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void NzImage::EnsureOwnership()
|
||||
{
|
||||
if (m_sharedImage == &emptyImage)
|
||||
@@ -1300,7 +1366,7 @@ void NzImage::EnsureOwnership()
|
||||
nzUInt8** pixels = new nzUInt8*[m_sharedImage->levelCount];
|
||||
for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i)
|
||||
{
|
||||
unsigned int size = GetSize(i);
|
||||
unsigned int size = GetMemoryUsage(i);
|
||||
pixels[i] = new nzUInt8[size];
|
||||
std::memcpy(pixels[i], m_sharedImage->pixels[i], size);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/IndexBuffer.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Utility/Algorithm.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/IndexIterator.hpp>
|
||||
@@ -11,13 +12,21 @@
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzIndexBuffer::NzIndexBuffer(bool largeIndices, NzBuffer* buffer)
|
||||
{
|
||||
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, nzBufferStorage storage, nzBufferUsage usage)
|
||||
NzIndexBuffer::NzIndexBuffer(bool largeIndices, unsigned int length, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
NzErrorFlags(nzErrorFlag_ThrowException, true);
|
||||
Reset(largeIndices, length, storage, usage);
|
||||
}
|
||||
|
||||
@@ -31,16 +40,6 @@ m_startOffset(indexBuffer.m_startOffset)
|
||||
{
|
||||
}
|
||||
|
||||
NzIndexBuffer::NzIndexBuffer(NzIndexBuffer&& indexBuffer) noexcept :
|
||||
NzRefCounted(),
|
||||
m_buffer(std::move(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()
|
||||
{
|
||||
NotifyDestroy();
|
||||
@@ -168,6 +167,11 @@ 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
|
||||
@@ -177,9 +181,9 @@ void NzIndexBuffer::Reset(bool largeIndices, NzBuffer* buffer, unsigned int star
|
||||
return;
|
||||
}
|
||||
|
||||
if (endOffset > startOffset)
|
||||
if (startOffset > endOffset)
|
||||
{
|
||||
NazaraError("End offset cannot be over start offset");
|
||||
NazaraError("Start offset cannot be over end offset");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,7 +210,7 @@ void NzIndexBuffer::Reset(bool largeIndices, NzBuffer* buffer, unsigned int star
|
||||
m_startOffset = startOffset;
|
||||
}
|
||||
|
||||
void NzIndexBuffer::Reset(bool largeIndices, unsigned int length, nzBufferStorage storage, nzBufferUsage usage)
|
||||
void NzIndexBuffer::Reset(bool largeIndices, unsigned int length, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
unsigned int stride = (largeIndices) ? sizeof(nzUInt32) : sizeof(nzUInt16);
|
||||
|
||||
@@ -228,16 +232,7 @@ void NzIndexBuffer::Reset(const NzIndexBuffer& indexBuffer)
|
||||
m_startOffset = indexBuffer.m_startOffset;
|
||||
}
|
||||
|
||||
void NzIndexBuffer::Reset(NzIndexBuffer&& indexBuffer) noexcept
|
||||
{
|
||||
m_buffer = std::move(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(nzBufferStorage storage)
|
||||
bool NzIndexBuffer::SetStorage(nzUInt32 storage)
|
||||
{
|
||||
return m_buffer->SetStorage(storage);
|
||||
}
|
||||
@@ -253,10 +248,3 @@ NzIndexBuffer& NzIndexBuffer::operator=(const NzIndexBuffer& indexBuffer)
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NzIndexBuffer& NzIndexBuffer::operator=(NzIndexBuffer&& indexBuffer) noexcept
|
||||
{
|
||||
Reset(indexBuffer);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
15
src/Nazara/Utility/Loaders/FreeType.hpp
Normal file
15
src/Nazara/Utility/Loaders/FreeType.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_LOADERS_FREETYPE_HPP
|
||||
#define NAZARA_LOADERS_FREETYPE_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_FreeType_Register();
|
||||
void NzLoaders_FreeType_Unregister();
|
||||
|
||||
#endif // NAZARA_LOADERS_FREETYPE_HPP
|
||||
454
src/Nazara/Utility/Loaders/FreeType/Loader.cpp
Normal file
454
src/Nazara/Utility/Loaders/FreeType/Loader.cpp
Normal file
@@ -0,0 +1,454 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq - 2009 Cruden BV
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/Loaders/FreeType.hpp>
|
||||
#include <freetype/ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_BITMAP_H
|
||||
#include FT_OUTLINE_H
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/InputStream.hpp>
|
||||
#include <Nazara/Core/MemoryStream.hpp>
|
||||
#include <Nazara/Utility/Font.hpp>
|
||||
#include <Nazara/Utility/FontData.hpp>
|
||||
#include <Nazara/Utility/FontGlyph.hpp>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
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)
|
||||
{
|
||||
// http://www.freetype.org/freetype2/docs/reference/ft2-system_interface.html#FT_Stream_IoFunc
|
||||
NzInputStream& inputStream = *static_cast<NzInputStream*>(stream->descriptor.pointer);
|
||||
|
||||
// La valeur de count indique une opération de lecture ou de positionnement
|
||||
if (count > 0)
|
||||
{
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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);
|
||||
embolden = false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool IsScalable() const override
|
||||
{
|
||||
return FT_IS_SCALABLE(m_face);
|
||||
}
|
||||
|
||||
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 NzString& filePath)
|
||||
{
|
||||
std::unique_ptr<NzFile> file(new NzFile);
|
||||
if (!file->Open(filePath, NzFile::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 = 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();
|
||||
}
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzFontParams& parameters)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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(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();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <Nazara/Core/Endianness.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/InputStream.hpp>
|
||||
#include <Nazara/Math/Basic.hpp>
|
||||
#include <Nazara/Math/Algorithm.hpp>
|
||||
#include <Nazara/Math/Quaternion.hpp>
|
||||
#include <Nazara/Utility/BufferMapper.hpp>
|
||||
#include <Nazara/Utility/Mesh.hpp>
|
||||
@@ -200,7 +200,10 @@ namespace
|
||||
{
|
||||
const unsigned int fixedIndex = indexFix[j];
|
||||
const MD2_TexCoord& texC = texCoords[triangles[i].texCoords[fixedIndex]];
|
||||
vertex[triangles[i].vertices[fixedIndex]].uv.Set(static_cast<float>(texC.u) / header.skinwidth, 1.f - static_cast<float>(texC.v)/header.skinheight);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -15,14 +15,69 @@ namespace
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzAnimationParams& parameters)
|
||||
{
|
||||
NzMD5AnimParser parser(stream, parameters);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
NzMD5AnimParser parser(stream);
|
||||
return parser.Check();
|
||||
}
|
||||
|
||||
bool Load(NzAnimation* animation, NzInputStream& stream, const NzAnimationParams& parameters)
|
||||
{
|
||||
NzMD5AnimParser parser(stream, parameters);
|
||||
return parser.Parse(animation);
|
||||
///TODO: Utiliser les paramètres
|
||||
NzMD5AnimParser parser(stream);
|
||||
|
||||
if (!parser.Parse())
|
||||
{
|
||||
NazaraError("MD5Anim parser failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int parent = joints[i].parent;
|
||||
for (unsigned int j = 0; j < frameCount; ++j)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/Loaders/MD5Anim/Parser.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Math/Basic.hpp>
|
||||
#include <Nazara/Math/Algorithm.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
@@ -13,9 +13,8 @@
|
||||
#include <limits>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzMD5AnimParser::NzMD5AnimParser(NzInputStream& stream, const NzAnimationParams& parameters) :
|
||||
NzMD5AnimParser::NzMD5AnimParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_parameters(parameters),
|
||||
m_keepLastLine(false),
|
||||
m_frameIndex(0),
|
||||
m_frameRate(0),
|
||||
@@ -47,7 +46,37 @@ nzTernary NzMD5AnimParser::Check()
|
||||
return nzTernary_False;
|
||||
}
|
||||
|
||||
bool NzMD5AnimParser::Parse(NzAnimation* animation)
|
||||
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))
|
||||
{
|
||||
@@ -204,47 +233,6 @@ bool NzMD5AnimParser::Parse(NzAnimation* animation)
|
||||
m_frameRate = 24;
|
||||
}
|
||||
|
||||
// À ce stade, nous sommes censés avoir assez d'informations pour créer l'animation
|
||||
if (!animation->CreateSkeletal(frameCount, jointCount))
|
||||
{
|
||||
NazaraError("Failed to create animation");
|
||||
return false;
|
||||
}
|
||||
|
||||
NzSequence sequence;
|
||||
sequence.firstFrame = 0;
|
||||
sequence.frameCount = m_frames.size();
|
||||
sequence.frameRate = m_frameRate;
|
||||
sequence.name = m_stream.GetPath().SubStringFrom(NAZARA_DIRECTORY_SEPARATOR, -1, true);
|
||||
if (!animation->AddSequence(sequence))
|
||||
NazaraWarning("Failed to add 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 = NzEulerAnglesf(-90.f, 90.f, 0.f);
|
||||
for (unsigned int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
int parent = m_joints[i].parent;
|
||||
for (unsigned int j = 0; j < frameCount; ++j)
|
||||
{
|
||||
NzSequenceJoint& sequenceJoint = sequenceJoints[j*jointCount + i];
|
||||
|
||||
if (parent >= 0)
|
||||
{
|
||||
sequenceJoint.position = m_frames[j].joints[i].pos;
|
||||
sequenceJoint.rotation = m_frames[j].joints[i].orient;
|
||||
}
|
||||
else
|
||||
{
|
||||
sequenceJoint.position = rotationQuat * m_frames[j].joints[i].pos;
|
||||
sequenceJoint.rotation = rotationQuat * m_frames[j].joints[i].orient;
|
||||
}
|
||||
|
||||
sequenceJoint.scale.Set(1.f);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -340,7 +328,7 @@ bool NzMD5AnimParser::ParseBounds()
|
||||
return false;
|
||||
}
|
||||
|
||||
m_frames[i].aabb.Set(min, max);
|
||||
m_frames[i].bounds.Set(min, max);
|
||||
}
|
||||
|
||||
if (!Advance())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -18,23 +18,16 @@
|
||||
class NzMD5AnimParser
|
||||
{
|
||||
public:
|
||||
NzMD5AnimParser(NzInputStream& stream, const NzAnimationParams& parameters);
|
||||
~NzMD5AnimParser();
|
||||
struct FrameJoint
|
||||
{
|
||||
NzQuaternionf orient;
|
||||
NzVector3f pos;
|
||||
};
|
||||
|
||||
nzTernary Check();
|
||||
bool Parse(NzAnimation* animation);
|
||||
|
||||
private:
|
||||
struct Frame
|
||||
{
|
||||
struct Joint
|
||||
{
|
||||
NzQuaternionf orient;
|
||||
NzVector3f pos;
|
||||
};
|
||||
|
||||
std::vector<Joint> joints;
|
||||
NzBoxf aabb;
|
||||
std::vector<FrameJoint> joints;
|
||||
NzBoxf bounds;
|
||||
};
|
||||
|
||||
struct Joint
|
||||
@@ -47,6 +40,21 @@ class NzMD5AnimParser
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
NzMD5AnimParser(NzInputStream& stream);
|
||||
~NzMD5AnimParser();
|
||||
|
||||
nzTernary Check();
|
||||
|
||||
unsigned int GetAnimatedComponentCount() const;
|
||||
const Frame* GetFrames() const;
|
||||
unsigned int GetFrameCount() const;
|
||||
unsigned int GetFrameRate() const;
|
||||
const Joint* GetJoints() const;
|
||||
unsigned int GetJointCount() const;
|
||||
|
||||
bool Parse();
|
||||
|
||||
private:
|
||||
bool Advance(bool required = true);
|
||||
void Error(const NzString& message);
|
||||
bool ParseBaseframe();
|
||||
@@ -61,7 +69,6 @@ class NzMD5AnimParser
|
||||
std::vector<Joint> m_joints;
|
||||
NzInputStream& m_stream;
|
||||
NzString m_currentLine;
|
||||
const NzAnimationParams& m_parameters;
|
||||
bool m_keepLastLine;
|
||||
unsigned int m_frameIndex;
|
||||
unsigned int m_frameRate;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/Loaders/MD5Mesh.hpp>
|
||||
#include <Nazara/Utility/Loaders/MD5Mesh/Parser.hpp>
|
||||
#include <Nazara/Utility/IndexIterator.hpp>
|
||||
#include <Nazara/Utility/IndexMapper.hpp>
|
||||
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <memory>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace
|
||||
@@ -15,14 +20,297 @@ namespace
|
||||
|
||||
nzTernary Check(NzInputStream& stream, const NzMeshParams& parameters)
|
||||
{
|
||||
NzMD5MeshParser parser(stream, parameters);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
NzMD5MeshParser parser(stream);
|
||||
return parser.Check();
|
||||
}
|
||||
|
||||
bool Load(NzMesh* mesh, NzInputStream& stream, const NzMeshParams& parameters)
|
||||
{
|
||||
NzMD5MeshParser parser(stream, parameters);
|
||||
return parser.Parse(mesh);
|
||||
NzMD5MeshParser parser(stream);
|
||||
if (!parser.Parse())
|
||||
{
|
||||
NazaraError("MD5Mesh parser failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
mesh->CreateSkeletal(jointCount);
|
||||
|
||||
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());
|
||||
|
||||
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(largeIndices, indexCount, parameters.storage));
|
||||
indexBuffer->SetPersistent(false);
|
||||
|
||||
std::unique_ptr<NzVertexBuffer> vertexBuffer(new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent_Skinning), vertexCount, parameters.storage, nzBufferUsage_Static));
|
||||
vertexBuffer->SetPersistent(false);
|
||||
|
||||
// Index buffer
|
||||
NzIndexMapper indexMapper(indexBuffer.get(), 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();
|
||||
|
||||
// Vertex buffer
|
||||
struct Weight
|
||||
{
|
||||
float bias;
|
||||
unsigned int jointIndex;
|
||||
};
|
||||
|
||||
std::vector<Weight> tempWeights;
|
||||
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), 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
|
||||
std::unique_ptr<NzSkeletalMesh> subMesh(new NzSkeletalMesh(mesh));
|
||||
subMesh->Create(vertexBuffer.get());
|
||||
vertexBuffer.release();
|
||||
|
||||
if (parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer.get());
|
||||
indexBuffer.release();
|
||||
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
subMesh->SetMaterialIndex(i);
|
||||
subMesh->SetPrimitiveMode(nzPrimitiveMode_TriangleList);
|
||||
|
||||
mesh->AddSubMesh(subMesh.get());
|
||||
subMesh.release();
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Index buffer
|
||||
bool largeIndices = (vertexCount > std::numeric_limits<nzUInt16>::max());
|
||||
|
||||
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(largeIndices, indexCount, parameters.storage));
|
||||
indexBuffer->SetPersistent(false);
|
||||
|
||||
NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_DiscardAndWrite);
|
||||
NzIndexIterator index = indexMapper.begin();
|
||||
|
||||
for (const NzMD5MeshParser::Triangle& triangle : md5Mesh.triangles)
|
||||
{
|
||||
// On les respécifie dans le bon ordre
|
||||
*index++ = triangle.x;
|
||||
*index++ = triangle.z;
|
||||
*index++ = triangle.y;
|
||||
}
|
||||
indexMapper.Unmap();
|
||||
|
||||
// Vertex buffer
|
||||
std::unique_ptr<NzVertexBuffer> vertexBuffer(new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage));
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||
|
||||
NzMeshVertex* vertex = reinterpret_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
||||
for (const NzMD5MeshParser::Vertex& md5Vertex : md5Mesh.vertices)
|
||||
{
|
||||
// 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];
|
||||
|
||||
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
|
||||
std::unique_ptr<NzStaticMesh> subMesh(new NzStaticMesh(mesh));
|
||||
subMesh->Create(vertexBuffer.get());
|
||||
|
||||
vertexBuffer->SetPersistent(false);
|
||||
vertexBuffer.release();
|
||||
|
||||
if (parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer.get());
|
||||
indexBuffer.release();
|
||||
|
||||
// Material
|
||||
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
|
||||
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
subMesh->SetMaterialIndex(i);
|
||||
|
||||
mesh->AddSubMesh(subMesh.get());
|
||||
subMesh.release();
|
||||
}
|
||||
|
||||
if (parameters.center)
|
||||
{
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
{
|
||||
NzStaticMesh* subMesh = static_cast<NzStaticMesh*>(mesh->GetSubMesh(i));
|
||||
subMesh->Center();
|
||||
}
|
||||
|
||||
mesh->InvalidateAABB();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/Loaders/MD5Mesh/Parser.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Math/Basic.hpp>
|
||||
#include <Nazara/Math/Algorithm.hpp>
|
||||
#include <Nazara/Utility/BufferMapper.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/IndexIterator.hpp>
|
||||
@@ -19,9 +19,8 @@
|
||||
#include <memory>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzMD5MeshParser::NzMD5MeshParser(NzInputStream& stream, const NzMeshParams& parameters) :
|
||||
NzMD5MeshParser::NzMD5MeshParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_parameters(parameters),
|
||||
m_keepLastLine(false),
|
||||
m_lineCount(0),
|
||||
m_meshIndex(0),
|
||||
@@ -52,7 +51,27 @@ nzTernary NzMD5MeshParser::Check()
|
||||
return nzTernary_False;
|
||||
}
|
||||
|
||||
bool NzMD5MeshParser::Parse(NzMesh* mesh)
|
||||
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))
|
||||
{
|
||||
@@ -151,271 +170,6 @@ bool NzMD5MeshParser::Parse(NzMesh* mesh)
|
||||
}
|
||||
}
|
||||
|
||||
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
||||
NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 180.f, 0.f);
|
||||
NzString baseDir = m_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(m_parameters.scale/40.f);
|
||||
|
||||
if (m_parameters.animated)
|
||||
{
|
||||
if (!mesh->CreateSkeletal(m_joints.size())) // Ne devrait jamais échouer
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
NzSkeleton* skeleton = mesh->GetSkeleton();
|
||||
for (unsigned int i = 0; i < m_joints.size(); ++i)
|
||||
{
|
||||
NzJoint* joint = skeleton->GetJoint(i);
|
||||
|
||||
int parent = m_joints[i].parent;
|
||||
if (parent >= 0)
|
||||
joint->SetParent(skeleton->GetJoint(parent));
|
||||
|
||||
joint->SetName(m_joints[i].name);
|
||||
|
||||
NzMatrix4f bindMatrix;
|
||||
|
||||
if (parent >= 0)
|
||||
bindMatrix.MakeTransform(m_joints[i].bindPos, m_joints[i].bindOrient);
|
||||
else
|
||||
bindMatrix.MakeTransform(rotationQuat * m_joints[i].bindPos, rotationQuat * m_joints[i].bindOrient);
|
||||
|
||||
joint->SetInverseBindMatrix(bindMatrix.InverseAffine());
|
||||
}
|
||||
|
||||
mesh->SetMaterialCount(m_meshes.size());
|
||||
for (unsigned int i = 0; i < m_meshes.size(); ++i)
|
||||
{
|
||||
const Mesh& md5Mesh = m_meshes[i];
|
||||
|
||||
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
||||
unsigned int vertexCount = md5Mesh.vertices.size();
|
||||
|
||||
bool largeIndices = (vertexCount > std::numeric_limits<nzUInt16>::max());
|
||||
|
||||
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(largeIndices, indexCount, m_parameters.storage));
|
||||
indexBuffer->SetPersistent(false);
|
||||
|
||||
std::unique_ptr<NzVertexBuffer> vertexBuffer(new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent_Skinning), vertexCount, m_parameters.storage, nzBufferUsage_Static));
|
||||
vertexBuffer->SetPersistent(false);
|
||||
|
||||
// Index buffer
|
||||
NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_DiscardAndWrite);
|
||||
|
||||
unsigned int index = 0;
|
||||
for (const Mesh::Triangle& triangle : md5Mesh.triangles)
|
||||
{
|
||||
// On les respécifie dans le bon ordre
|
||||
indexMapper.Set(index++, triangle.x);
|
||||
indexMapper.Set(index++, triangle.z);
|
||||
indexMapper.Set(index++, triangle.y);
|
||||
}
|
||||
|
||||
indexMapper.Unmap();
|
||||
|
||||
// Vertex buffer
|
||||
struct Weight
|
||||
{
|
||||
float bias;
|
||||
unsigned int jointIndex;
|
||||
};
|
||||
|
||||
std::vector<Weight> tempWeights(NAZARA_UTILITY_SKINNING_MAX_WEIGHTS);
|
||||
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||
NzSkeletalMeshVertex* vertices = static_cast<NzSkeletalMeshVertex*>(vertexMapper.GetPointer());
|
||||
for (const Mesh::Vertex& vertex : md5Mesh.vertices)
|
||||
{
|
||||
// Skinning MD5 (Formule d'Id Tech)
|
||||
NzVector3f finalPos(NzVector3f::Zero());
|
||||
|
||||
tempWeights.resize(vertex.weightCount);
|
||||
for (unsigned int j = 0; j < vertex.weightCount; ++j)
|
||||
{
|
||||
const Mesh::Weight& weight = md5Mesh.weights[vertex.startWeight + j];
|
||||
const Joint& joint = m_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};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// On donne une valeur aux poids présents, et 0 pour les autres (nécessaire pour le GPU Skinning)
|
||||
if (j < weightCount)
|
||||
{
|
||||
vertices->weights[j] = tempWeights[j].bias;
|
||||
vertices->jointIndexes[j] = tempWeights[j].jointIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices->weights[j] = 0.f;
|
||||
vertices->jointIndexes[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vertices->position = finalPos;
|
||||
vertices->uv.Set(vertex.uv.x, 1.f-vertex.uv.y);
|
||||
vertices++;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
// Material
|
||||
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
|
||||
|
||||
// Submesh
|
||||
std::unique_ptr<NzSkeletalMesh> subMesh(new NzSkeletalMesh(mesh));
|
||||
if (!subMesh->Create(vertexBuffer.get()))
|
||||
{
|
||||
NazaraError("Failed to create skeletal mesh");
|
||||
continue;
|
||||
}
|
||||
vertexBuffer.release();
|
||||
|
||||
if (m_parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer.get());
|
||||
indexBuffer.release();
|
||||
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
subMesh->SetMaterialIndex(i);
|
||||
subMesh->SetPrimitiveMode(nzPrimitiveMode_TriangleList);
|
||||
|
||||
mesh->AddSubMesh(subMesh.get());
|
||||
subMesh.release();
|
||||
|
||||
// Animation
|
||||
// Il est peut-être éventuellement possible que la probabilité que l'animation ait le même nom soit non-nulle.
|
||||
NzString path = m_stream.GetPath();
|
||||
if (!path.IsEmpty())
|
||||
{
|
||||
path.Replace(".md5mesh", ".md5anim", -8, NzString::CaseInsensitive);
|
||||
if (NzFile::Exists(path))
|
||||
mesh->SetAnimation(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
mesh->SetMaterialCount(m_meshes.size());
|
||||
for (unsigned int i = 0; i < m_meshes.size(); ++i)
|
||||
{
|
||||
const Mesh& md5Mesh = m_meshes[i];
|
||||
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
||||
unsigned int vertexCount = md5Mesh.vertices.size();
|
||||
|
||||
// Index buffer
|
||||
bool largeIndices = (vertexCount > std::numeric_limits<nzUInt16>::max());
|
||||
|
||||
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(largeIndices, indexCount, m_parameters.storage));
|
||||
indexBuffer->SetPersistent(false);
|
||||
|
||||
NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_DiscardAndWrite);
|
||||
NzIndexIterator index = indexMapper.begin();
|
||||
|
||||
for (const Mesh::Triangle& triangle : md5Mesh.triangles)
|
||||
{
|
||||
// On les respécifie dans le bon ordre
|
||||
*index++ = triangle.x;
|
||||
*index++ = triangle.z;
|
||||
*index++ = triangle.y;
|
||||
}
|
||||
indexMapper.Unmap();
|
||||
|
||||
// Vertex buffer
|
||||
std::unique_ptr<NzVertexBuffer> vertexBuffer(new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent), vertexCount, m_parameters.storage));
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||
|
||||
NzMeshVertex* vertex = reinterpret_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
||||
for (const Mesh::Vertex& md5Vertex : md5Mesh.vertices)
|
||||
{
|
||||
// Skinning MD5 (Formule d'Id Tech)
|
||||
NzVector3f finalPos(NzVector3f::Zero());
|
||||
for (unsigned int j = 0; j < md5Vertex.weightCount; ++j)
|
||||
{
|
||||
const Mesh::Weight& weight = md5Mesh.weights[md5Vertex.startWeight + j];
|
||||
const Joint& joint = m_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, 1.f - md5Vertex.uv.y);
|
||||
vertex++;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
// Submesh
|
||||
std::unique_ptr<NzStaticMesh> subMesh(new NzStaticMesh(mesh));
|
||||
if (!subMesh->Create(vertexBuffer.get()))
|
||||
{
|
||||
NazaraError("Failed to create static submesh");
|
||||
continue;
|
||||
}
|
||||
|
||||
vertexBuffer->SetPersistent(false);
|
||||
vertexBuffer.release();
|
||||
|
||||
if (m_parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer.get());
|
||||
indexBuffer.release();
|
||||
|
||||
// Material
|
||||
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
|
||||
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
subMesh->SetMaterialIndex(i);
|
||||
|
||||
if (m_parameters.center)
|
||||
subMesh->Center();
|
||||
|
||||
mesh->AddSubMesh(subMesh.get());
|
||||
subMesh.release();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -555,7 +309,7 @@ bool NzMD5MeshParser::ParseMesh()
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Mesh::Triangle& triangle = m_meshes[m_meshIndex].triangles[i];
|
||||
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)
|
||||
{
|
||||
@@ -578,7 +332,7 @@ bool NzMD5MeshParser::ParseMesh()
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Mesh::Vertex& vertex = m_meshes[m_meshIndex].vertices[i];
|
||||
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)
|
||||
{
|
||||
@@ -601,7 +355,7 @@ bool NzMD5MeshParser::ParseMesh()
|
||||
if (!Advance())
|
||||
return false;
|
||||
|
||||
Mesh::Weight& weight = m_meshes[m_meshIndex].weights[i];
|
||||
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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -18,13 +18,6 @@
|
||||
class NzMD5MeshParser
|
||||
{
|
||||
public:
|
||||
NzMD5MeshParser(NzInputStream& stream, const NzMeshParams& parameters);
|
||||
~NzMD5MeshParser();
|
||||
|
||||
nzTernary Check();
|
||||
bool Parse(NzMesh* mesh);
|
||||
|
||||
private:
|
||||
struct Joint
|
||||
{
|
||||
NzQuaternionf bindOrient;
|
||||
@@ -33,30 +26,43 @@ class NzMD5MeshParser
|
||||
int parent;
|
||||
};
|
||||
|
||||
typedef NzVector3ui Triangle;
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
NzVector2f uv;
|
||||
unsigned int startWeight;
|
||||
unsigned int weightCount;
|
||||
};
|
||||
|
||||
struct Weight
|
||||
{
|
||||
NzVector3f pos;
|
||||
float bias;
|
||||
unsigned int joint;
|
||||
};
|
||||
|
||||
struct Mesh
|
||||
{
|
||||
typedef NzVector3ui Triangle;
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
NzVector2f uv;
|
||||
unsigned int startWeight;
|
||||
unsigned int weightCount;
|
||||
};
|
||||
|
||||
struct Weight
|
||||
{
|
||||
NzVector3f pos;
|
||||
float bias;
|
||||
unsigned int joint;
|
||||
};
|
||||
|
||||
std::vector<Triangle> triangles;
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<Weight> weights;
|
||||
NzString shader;
|
||||
};
|
||||
|
||||
NzMD5MeshParser(NzInputStream& stream);
|
||||
~NzMD5MeshParser();
|
||||
|
||||
nzTernary Check();
|
||||
|
||||
const Joint* GetJoints() const;
|
||||
unsigned int GetJointCount() const;
|
||||
const Mesh* GetMeshes() const;
|
||||
unsigned int GetMeshCount() const;
|
||||
|
||||
bool Parse();
|
||||
|
||||
private:
|
||||
bool Advance(bool required = true);
|
||||
void Error(const NzString& message);
|
||||
bool ParseJoints();
|
||||
@@ -68,7 +74,6 @@ class NzMD5MeshParser
|
||||
std::vector<Mesh> m_meshes;
|
||||
NzInputStream& m_stream;
|
||||
NzString m_currentLine;
|
||||
const NzMeshParams& m_parameters;
|
||||
bool m_keepLastLine;
|
||||
unsigned int m_lineCount;
|
||||
unsigned int m_meshIndex;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -287,7 +287,7 @@ namespace
|
||||
/* for each color plane */
|
||||
for (int c = 0; c < 3; ++c)
|
||||
{
|
||||
nzUInt8* ptr = &pixels[y * width * 4];
|
||||
nzUInt8* ptr = &pixels[y * width * 3];
|
||||
int bytes = header.bytesPerScanLine;
|
||||
|
||||
/* decode line number y */
|
||||
@@ -324,7 +324,7 @@ namespace
|
||||
}
|
||||
|
||||
default:
|
||||
NazaraError("Failed to load " + NzString::Number(bitCount) + " bitcount pcx files");
|
||||
NazaraError("Unsupported " + NzString::Number(bitCount) + " bitcount for pcx files");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -15,29 +15,29 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
int Read(void* userdata, char* data, int size)
|
||||
int Read(void* userdata, char* data, int size)
|
||||
{
|
||||
NzInputStream* stream = static_cast<NzInputStream*>(userdata);
|
||||
return static_cast<int>(stream->Read(data, size));
|
||||
}
|
||||
|
||||
void Skip(void* userdata, int size)
|
||||
{
|
||||
NzInputStream* stream = reinterpret_cast<NzInputStream*>(userdata);
|
||||
return static_cast<int>(stream->Read(data, size));
|
||||
NzInputStream* stream = static_cast<NzInputStream*>(userdata);
|
||||
stream->SetCursorPos(static_cast<nzInt64>(stream->GetCursorPos()) + static_cast<nzInt64>(size));
|
||||
}
|
||||
|
||||
void Skip(void* userdata, unsigned int size)
|
||||
{
|
||||
NzInputStream* stream = reinterpret_cast<NzInputStream*>(userdata);
|
||||
stream->Read(nullptr, size);
|
||||
}
|
||||
|
||||
int Eof(void* userdata)
|
||||
{
|
||||
NzInputStream* stream = reinterpret_cast<NzInputStream*>(userdata);
|
||||
return stream->GetCursorPos() >= stream->GetSize();
|
||||
}
|
||||
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", "psd", "tga"};
|
||||
static std::set<NzString> supportedExtensions = {"bmp", "gif", "hdr", "jpg", "jpeg", "pic", "png", "ppm", "pgm", "psd", "tga"};
|
||||
return supportedExtensions.find(extension) != supportedExtensions.end();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <Nazara/Core/Enums.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/PrimitiveList.hpp>
|
||||
#include <Nazara/Math/Basic.hpp>
|
||||
#include <Nazara/Math/Algorithm.hpp>
|
||||
#include <Nazara/Utility/Algorithm.hpp>
|
||||
#include <Nazara/Utility/Animation.hpp>
|
||||
#include <Nazara/Utility/Buffer.hpp>
|
||||
@@ -25,13 +25,13 @@
|
||||
|
||||
NzMeshParams::NzMeshParams()
|
||||
{
|
||||
if (!NzBuffer::IsSupported(storage))
|
||||
storage = nzBufferStorage_Software;
|
||||
if (!NzBuffer::IsStorageSupported(storage))
|
||||
storage = nzDataStorage_Software;
|
||||
}
|
||||
|
||||
bool NzMeshParams::IsValid() const
|
||||
{
|
||||
if (!NzBuffer::IsSupported(storage))
|
||||
if (!NzBuffer::IsStorageSupported(storage))
|
||||
{
|
||||
NazaraError("Storage not supported");
|
||||
return false;
|
||||
@@ -55,7 +55,7 @@ struct NzMeshImpl
|
||||
|
||||
std::unordered_map<NzString, unsigned int> subMeshMap;
|
||||
std::vector<NzString> materials;
|
||||
std::vector<NzSubMesh*> subMeshes;
|
||||
std::vector<NzSubMeshRef> subMeshes;
|
||||
nzAnimationType animationType;
|
||||
NzBoxf aabb;
|
||||
NzSkeleton skeleton; // Uniquement pour les meshs squelettiques
|
||||
@@ -91,9 +91,6 @@ void NzMesh::AddSubMesh(NzSubMesh* subMesh)
|
||||
}
|
||||
#endif
|
||||
|
||||
subMesh->AddObjectListener(this, m_impl->subMeshes.size());
|
||||
subMesh->AddReference();
|
||||
|
||||
m_impl->aabbUpdated = false; // On invalide l'AABB
|
||||
m_impl->subMeshes.push_back(subMesh);
|
||||
}
|
||||
@@ -135,9 +132,6 @@ void NzMesh::AddSubMesh(const NzString& identifier, NzSubMesh* subMesh)
|
||||
|
||||
int index = m_impl->subMeshes.size();
|
||||
|
||||
subMesh->AddObjectListener(this, index);
|
||||
subMesh->AddReference();
|
||||
|
||||
m_impl->aabbUpdated = false; // On invalide l'AABB
|
||||
m_impl->subMeshes.push_back(subMesh);
|
||||
m_impl->subMeshMap[identifier] = index;
|
||||
@@ -371,12 +365,6 @@ void NzMesh::Destroy()
|
||||
{
|
||||
NotifyDestroy();
|
||||
|
||||
for (NzSubMesh* subMesh : m_impl->subMeshes)
|
||||
{
|
||||
subMesh->RemoveObjectListener(this);
|
||||
subMesh->RemoveReference();
|
||||
}
|
||||
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
@@ -863,12 +851,6 @@ void NzMesh::RemoveSubMesh(const NzString& identifier)
|
||||
// On déplace l'itérateur du début d'une distance de x
|
||||
auto it2 = m_impl->subMeshes.begin();
|
||||
std::advance(it2, index);
|
||||
|
||||
// On libère la ressource
|
||||
NzSubMesh* subMesh = *it2;
|
||||
subMesh->RemoveObjectListener(this);
|
||||
subMesh->RemoveReference();
|
||||
|
||||
m_impl->subMeshes.erase(it2);
|
||||
|
||||
m_impl->aabbUpdated = false; // On invalide l'AABB
|
||||
@@ -893,12 +875,6 @@ void NzMesh::RemoveSubMesh(unsigned int index)
|
||||
// On déplace l'itérateur du début de x
|
||||
auto it = m_impl->subMeshes.begin();
|
||||
std::advance(it, index);
|
||||
|
||||
// On libère la ressource
|
||||
NzSubMesh* subMesh = *it;
|
||||
subMesh->RemoveObjectListener(this);
|
||||
subMesh->RemoveReference();
|
||||
|
||||
m_impl->subMeshes.erase(it);
|
||||
|
||||
m_impl->aabbUpdated = false; // On invalide l'AABB
|
||||
@@ -1011,11 +987,4 @@ void NzMesh::Transform(const NzMatrix4f& matrix)
|
||||
m_impl->aabbUpdated = false;
|
||||
}
|
||||
|
||||
void NzMesh::OnObjectReleased(const NzRefCounted* object, int index)
|
||||
{
|
||||
NazaraUnused(object);
|
||||
|
||||
RemoveSubMesh(index);
|
||||
}
|
||||
|
||||
NzMeshLoader::LoaderList NzMesh::s_loaders;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -125,11 +125,6 @@ NzVector3f NzNode::GetLeft() const
|
||||
return m_derivedRotation * NzVector3f::Left();
|
||||
}
|
||||
|
||||
const NzString& NzNode::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
nzNodeType NzNode::GetNodeType() const
|
||||
{
|
||||
return nzNodeType_Default;
|
||||
@@ -270,7 +265,7 @@ NzNode& NzNode::Move(const NzVector3f& movement, nzCoordSys coordSys)
|
||||
}
|
||||
|
||||
case nzCoordSys_Local:
|
||||
m_position += m_scale * (m_rotation * movement);
|
||||
m_position += m_rotation * movement;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -413,11 +408,6 @@ void NzNode::SetInitialScale(float scaleX, float scaleY, float scaleZ)
|
||||
InvalidateNode();
|
||||
}
|
||||
|
||||
void NzNode::SetName(const NzString& name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
void NzNode::SetParent(const NzNode* node, bool keepDerived)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
@@ -621,7 +611,6 @@ NzNode& NzNode::operator=(const NzNode& node)
|
||||
m_initialPosition = node.m_initialPosition;
|
||||
m_initialRotation = node.m_initialRotation;
|
||||
m_initialScale = node.m_initialScale;
|
||||
m_name = node.m_name;
|
||||
m_position = node.m_position;
|
||||
m_rotation = node.m_rotation;
|
||||
m_scale = node.m_scale;
|
||||
@@ -685,7 +674,7 @@ void NzNode::UpdateDerived() const
|
||||
m_derivedRotation.Normalize();
|
||||
}
|
||||
else
|
||||
m_derivedRotation = m_initialRotation * m_rotation; ///FIXME: Besoin d'une normalisation ?
|
||||
m_derivedRotation = m_initialRotation * m_rotation;
|
||||
|
||||
m_derivedScale = m_initialScale * m_scale;
|
||||
if (m_inheritScale)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -50,6 +50,94 @@ namespace
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**********************************A8***********************************/
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_A8, nzPixelFormat_BGRA8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
*dst++ = 0xFF;
|
||||
*dst++ = 0xFF;
|
||||
*dst++ = 0xFF;
|
||||
*dst++ = *start;
|
||||
|
||||
start += 1;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_A8, nzPixelFormat_LA8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
*dst++ = 0xFF;
|
||||
*dst++ = *start;
|
||||
|
||||
start += 1;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_A8, nzPixelFormat_RGB5A1>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
nzUInt16* ptr = reinterpret_cast<nzUInt16*>(dst);
|
||||
while (start < end)
|
||||
{
|
||||
*ptr = (static_cast<nzUInt16>(0x1F) << 11) |
|
||||
(static_cast<nzUInt16>(0x1F) << 6) |
|
||||
(static_cast<nzUInt16>(0x1F) << 1) |
|
||||
((*start > 0xF) ? 1 : 0); // > 128
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(ptr, sizeof(nzUInt16));
|
||||
#endif
|
||||
|
||||
ptr++;
|
||||
start += 1;
|
||||
}
|
||||
|
||||
return reinterpret_cast<nzUInt8*>(ptr);
|
||||
}
|
||||
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_A8, nzPixelFormat_RGBA4>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
nzUInt16* ptr = reinterpret_cast<nzUInt16*>(dst);
|
||||
while (start < end)
|
||||
{
|
||||
*ptr = 0xFFF0 | c8to4(*start);
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(ptr, sizeof(nzUInt16));
|
||||
#endif
|
||||
|
||||
ptr++;
|
||||
start += 1;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_A8, nzPixelFormat_RGBA8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
*dst++ = 0xFF;
|
||||
*dst++ = 0xFF;
|
||||
*dst++ = 0xFF;
|
||||
*dst++ = *start;
|
||||
|
||||
start += 1;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**********************************BGR8***********************************/
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_BGR8, nzPixelFormat_BGRA8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
@@ -170,6 +258,19 @@ namespace
|
||||
}
|
||||
|
||||
/**********************************BGRA8**********************************/
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_BGRA8, nzPixelFormat_A8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
*dst++ = start[3];
|
||||
|
||||
start += 4;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_BGRA8, nzPixelFormat_BGR8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
@@ -243,7 +344,7 @@ namespace
|
||||
*ptr = (static_cast<nzUInt16>(c8to5(start[2])) << 11) |
|
||||
(static_cast<nzUInt16>(c8to5(start[1])) << 6) |
|
||||
(static_cast<nzUInt16>(c8to5(start[0])) << 1) |
|
||||
((start[3] == 0xFF) ? 1 : 0);
|
||||
((start[3] > 0xF) ? 1 : 0); // > 128
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(ptr, sizeof(nzUInt16));
|
||||
@@ -413,6 +514,19 @@ namespace
|
||||
}
|
||||
|
||||
/***********************************LA8***********************************/
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_LA8, nzPixelFormat_A8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
*dst++ = start[1];
|
||||
|
||||
start += 2;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_LA8, nzPixelFormat_BGR8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
@@ -465,7 +579,7 @@ namespace
|
||||
{
|
||||
nzUInt16 l = static_cast<nzUInt16>(c8to5(start[0]));
|
||||
|
||||
*ptr = (l << 11) | (l << 6) | (l << 1) | ((start[1] == 0xFF) ? 1 : 0);
|
||||
*ptr = (l << 11) | (l << 6) | (l << 1) | ((start[1] > 0xF) ? 1 : 0);
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(ptr, sizeof(nzUInt16));
|
||||
@@ -531,6 +645,25 @@ namespace
|
||||
}
|
||||
|
||||
/*********************************RGBA4***********************************/
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_RGBA4, nzPixelFormat_A8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
nzUInt16 pixel = *reinterpret_cast<const nzUInt16*>(start);
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(&pixel, sizeof(nzUInt16));
|
||||
#endif
|
||||
|
||||
*dst++ = c4to8(pixel & 0x000F);
|
||||
|
||||
start += 2;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_RGBA4, nzPixelFormat_BGR8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
@@ -638,7 +771,7 @@ namespace
|
||||
nzUInt16 b = c4to5((pixel & 0x00F0) >> 4);
|
||||
nzUInt16 a = c4to5((pixel & 0x000F) >> 0);
|
||||
|
||||
*ptr = (r << 11) | (g << 6) | (b << 1) | ((a == 0xFF) ? 1 : 0);
|
||||
*ptr = (r << 11) | (g << 6) | (b << 1) | ((a > 0x3) ? 1 : 0);
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(ptr, sizeof(nzUInt16));
|
||||
@@ -695,6 +828,25 @@ namespace
|
||||
}
|
||||
|
||||
/*********************************RGB5A1**********************************/
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_RGB5A1, nzPixelFormat_A8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
nzUInt16 pixel = *reinterpret_cast<const nzUInt16*>(start);
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(&pixel, sizeof(nzUInt16));
|
||||
#endif
|
||||
|
||||
*dst++ = static_cast<nzUInt8>((pixel & 0x1)*0xFF);
|
||||
|
||||
start += 2;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_RGB5A1, nzPixelFormat_BGR8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
@@ -977,6 +1129,19 @@ namespace
|
||||
}
|
||||
|
||||
/**********************************RGBA8**********************************/
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_RGBA8, nzPixelFormat_A8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
*dst++ = start[3];
|
||||
|
||||
start += 4;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<>
|
||||
nzUInt8* ConvertPixels<nzPixelFormat_RGBA8, nzPixelFormat_BGR8>(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst)
|
||||
{
|
||||
@@ -1044,7 +1209,7 @@ namespace
|
||||
*ptr = (static_cast<nzUInt16>(c8to5(start[0])) << 11) |
|
||||
(static_cast<nzUInt16>(c8to5(start[1])) << 6) |
|
||||
(static_cast<nzUInt16>(c8to5(start[2])) << 1) |
|
||||
((start[3] == 0xFF) ? 1 : 0);
|
||||
((start[3] > 0xF) ? 1 : 0); // > 128
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
NzByteSwap(ptr, sizeof(nzUInt16));
|
||||
@@ -1106,6 +1271,13 @@ bool NzPixelFormat::Initialize()
|
||||
// Réinitialisation
|
||||
std::memset(s_convertFunctions, 0, (nzPixelFormat_Max+1)*(nzPixelFormat_Max+1)*sizeof(NzPixelFormat::ConvertFunction));
|
||||
|
||||
/***********************************A8************************************/
|
||||
RegisterConverter<nzPixelFormat_A8, nzPixelFormat_BGRA8>();
|
||||
RegisterConverter<nzPixelFormat_A8, nzPixelFormat_LA8>();
|
||||
RegisterConverter<nzPixelFormat_A8, nzPixelFormat_RGB5A1>();
|
||||
RegisterConverter<nzPixelFormat_A8, nzPixelFormat_RGBA4>();
|
||||
RegisterConverter<nzPixelFormat_A8, nzPixelFormat_RGBA8>();
|
||||
|
||||
/**********************************BGR8***********************************/
|
||||
RegisterConverter<nzPixelFormat_BGR8, nzPixelFormat_BGRA8>();
|
||||
RegisterConverter<nzPixelFormat_BGR8, nzPixelFormat_L8>();
|
||||
@@ -1124,6 +1296,7 @@ bool NzPixelFormat::Initialize()
|
||||
RegisterConverter<nzPixelFormat_BGR8, nzPixelFormat_RGBA8>();
|
||||
|
||||
/**********************************BGRA8**********************************/
|
||||
RegisterConverter<nzPixelFormat_BGRA8, nzPixelFormat_A8>();
|
||||
RegisterConverter<nzPixelFormat_BGRA8, nzPixelFormat_BGR8>();
|
||||
RegisterConverter<nzPixelFormat_BGRA8, nzPixelFormat_L8>();
|
||||
RegisterConverter<nzPixelFormat_BGRA8, nzPixelFormat_LA8>();/*
|
||||
@@ -1227,6 +1400,7 @@ bool NzPixelFormat::Initialize()
|
||||
RegisterConverter<nzPixelFormat_L8, nzPixelFormat_RGBA8>();
|
||||
|
||||
/***********************************LA8***********************************/
|
||||
RegisterConverter<nzPixelFormat_LA8, nzPixelFormat_A8>();
|
||||
RegisterConverter<nzPixelFormat_LA8, nzPixelFormat_BGR8>();
|
||||
RegisterConverter<nzPixelFormat_LA8, nzPixelFormat_BGRA8>();
|
||||
RegisterConverter<nzPixelFormat_LA8, nzPixelFormat_L8>();/*
|
||||
@@ -1244,6 +1418,7 @@ bool NzPixelFormat::Initialize()
|
||||
RegisterConverter<nzPixelFormat_LA8, nzPixelFormat_RGBA8>();
|
||||
|
||||
/**********************************RGBA4**********************************/
|
||||
RegisterConverter<nzPixelFormat_RGBA4, nzPixelFormat_A8>();
|
||||
RegisterConverter<nzPixelFormat_RGBA4, nzPixelFormat_BGR8>();
|
||||
RegisterConverter<nzPixelFormat_RGBA4, nzPixelFormat_BGRA8>();
|
||||
RegisterConverter<nzPixelFormat_RGBA4, nzPixelFormat_L8>();
|
||||
@@ -1261,6 +1436,7 @@ bool NzPixelFormat::Initialize()
|
||||
RegisterConverter<nzPixelFormat_RGBA4, nzPixelFormat_RGBA8>();
|
||||
|
||||
/*********************************RGB5A1**********************************/
|
||||
RegisterConverter<nzPixelFormat_RGB5A1, nzPixelFormat_A8>();
|
||||
RegisterConverter<nzPixelFormat_RGB5A1, nzPixelFormat_BGR8>();
|
||||
RegisterConverter<nzPixelFormat_RGB5A1, nzPixelFormat_BGRA8>();
|
||||
RegisterConverter<nzPixelFormat_RGB5A1, nzPixelFormat_L8>();
|
||||
@@ -1295,6 +1471,7 @@ bool NzPixelFormat::Initialize()
|
||||
RegisterConverter<nzPixelFormat_RGB8, nzPixelFormat_RGBA8>();
|
||||
|
||||
/**********************************RGBA8**********************************/
|
||||
RegisterConverter<nzPixelFormat_RGBA8, nzPixelFormat_A8>();
|
||||
RegisterConverter<nzPixelFormat_RGBA8, nzPixelFormat_BGR8>();
|
||||
RegisterConverter<nzPixelFormat_RGBA8, nzPixelFormat_BGRA8>();
|
||||
RegisterConverter<nzPixelFormat_RGBA8, nzPixelFormat_L8>();
|
||||
|
||||
BIN
src/Nazara/Utility/Resources/Fonts/Cabin-Regular.ttf
Normal file
BIN
src/Nazara/Utility/Resources/Fonts/Cabin-Regular.ttf
Normal file
Binary file not shown.
1
src/Nazara/Utility/Resources/Fonts/Cabin-Regular.ttf.h
Normal file
1
src/Nazara/Utility/Resources/Fonts/Cabin-Regular.ttf.h
Normal file
File diff suppressed because one or more lines are too long
302
src/Nazara/Utility/SimpleTextDrawer.cpp
Normal file
302
src/Nazara/Utility/SimpleTextDrawer.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/SimpleTextDrawer.hpp>
|
||||
#include <memory>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzSimpleTextDrawer::NzSimpleTextDrawer() :
|
||||
m_color(NzColor::White),
|
||||
m_fontListener(this),
|
||||
m_style(nzTextStyle_Regular)
|
||||
{
|
||||
SetFont(NzFont::GetDefault());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
nzUInt32 NzSimpleTextDrawer::GetStyle() const
|
||||
{
|
||||
return m_style;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_font = font;
|
||||
m_fontListener = font;
|
||||
|
||||
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::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;
|
||||
}
|
||||
|
||||
NzFont* NzSimpleTextDrawer::GetFont(unsigned int index) const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (index > 0)
|
||||
{
|
||||
NazaraError("Font index out of range (" + NzString::Number(index) + " >= 1)");
|
||||
return nullptr;
|
||||
}
|
||||
#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();
|
||||
}
|
||||
|
||||
bool NzSimpleTextDrawer::OnObjectModified(const NzRefCounted* object, int index, unsigned int code)
|
||||
{
|
||||
NazaraUnused(object);
|
||||
NazaraUnused(index);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (m_font != object)
|
||||
{
|
||||
NazaraInternalError("Not listening to " + NzString::Pointer(object));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (code == NzFont::ModificationCode_AtlasChanged ||
|
||||
code == NzFont::ModificationCode_AtlasLayerChanged ||
|
||||
code == NzFont::ModificationCode_GlyphCacheCleared)
|
||||
{
|
||||
m_glyphUpdated = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzSimpleTextDrawer::OnObjectReleased(const NzRefCounted* object, int index)
|
||||
{
|
||||
NazaraUnused(object);
|
||||
NazaraUnused(index);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (m_font != object)
|
||||
{
|
||||
NazaraInternalError("Not listening to " + NzString::Pointer(object));
|
||||
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 ?)
|
||||
unsigned int size;
|
||||
std::unique_ptr<char32_t[]> characters(m_text.GetUtf32Buffer(&size));
|
||||
if (!characters)
|
||||
{
|
||||
NazaraError("Invalid character set");
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
m_glyphs.reserve(size);
|
||||
nzUInt32 previousCharacter = 0;
|
||||
for (unsigned int i = 0; i < size; ++i)
|
||||
{
|
||||
char32_t character = characters[i];
|
||||
|
||||
if (previousCharacter != 0)
|
||||
drawPos.x += m_font->GetKerning(m_characterSize, previousCharacter, character);
|
||||
|
||||
previousCharacter = character;
|
||||
|
||||
bool whitespace = true;
|
||||
switch (character)
|
||||
{
|
||||
case ' ':
|
||||
drawPos.x += sizeInfo.spaceAdvance;
|
||||
break;
|
||||
|
||||
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 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
|
||||
|
||||
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;
|
||||
|
||||
NzRectf 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)
|
||||
NzVector2f center = bounds.GetCenter();
|
||||
|
||||
bounds.width *= 1.1f;
|
||||
bounds.height *= 1.1f;
|
||||
|
||||
// On le replace à la bonne hauteur
|
||||
NzVector2f 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 j = 0; j < 4; ++j)
|
||||
textBounds.ExtendTo(glyph.corners[j]);
|
||||
|
||||
drawPos.x += advance;
|
||||
m_glyphs.push_back(glyph);
|
||||
}
|
||||
|
||||
m_bounds.Set(std::floor(textBounds.x), std::floor(textBounds.y), std::ceil(textBounds.width), std::ceil(textBounds.height));
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -32,7 +32,7 @@ bool NzSkeleton::Create(unsigned int jointCount)
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (jointCount == 0)
|
||||
{
|
||||
NazaraError("Joint count must be over 0");
|
||||
NazaraError("Joint count must be over zero");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -23,7 +23,9 @@ NzStaticMesh::~NzStaticMesh()
|
||||
|
||||
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);
|
||||
///DOC: Invalider l'AABB après ça
|
||||
NzBoxf aabb(m_parent->GetAABB());
|
||||
NzVector3f offset(aabb.x + aabb.width/2.f, aabb.y + aabb.height/2.f, aabb.z + aabb.depth/2.f);
|
||||
|
||||
NzVertexMapper mapper(this);
|
||||
NzSparsePtr<NzVector3f> position = mapper.GetComponentPtr<NzVector3f>(nzVertexComponent_Position);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -46,8 +46,8 @@ bool NzTriangleIterator::Advance()
|
||||
|
||||
case nzPrimitiveMode_TriangleStrip:
|
||||
m_triangleIndices[2] = m_indexMapper.Get(m_currentIndex++);
|
||||
m_triangleIndices[1] = m_triangleIndices[2];
|
||||
m_triangleIndices[0] = m_triangleIndices[1];
|
||||
m_triangleIndices[1] = m_triangleIndices[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <Nazara/Core/Thread.hpp>
|
||||
#include <Nazara/Utility/Buffer.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/Font.hpp>
|
||||
#include <Nazara/Utility/Loaders/FreeType.hpp>
|
||||
#include <Nazara/Utility/Loaders/MD2.hpp>
|
||||
#include <Nazara/Utility/Loaders/MD5Anim.hpp>
|
||||
#include <Nazara/Utility/Loaders/MD5Mesh.hpp>
|
||||
@@ -48,6 +50,12 @@ bool NzUtility::Initialize()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NzFont::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize fonts");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NzPixelFormat::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize pixel formats");
|
||||
@@ -70,6 +78,9 @@ bool NzUtility::Initialize()
|
||||
// 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)
|
||||
|
||||
@@ -109,6 +120,7 @@ void NzUtility::Uninitialize()
|
||||
// Libération du module
|
||||
s_moduleReferenceCounter = 0;
|
||||
|
||||
NzLoaders_FreeType_Unregister();
|
||||
NzLoaders_MD2_Unregister();
|
||||
NzLoaders_MD5Anim_Unregister();
|
||||
NzLoaders_MD5Mesh_Unregister();
|
||||
@@ -118,6 +130,7 @@ void NzUtility::Uninitialize()
|
||||
NzWindow::Uninitialize();
|
||||
NzVertexDeclaration::Uninitialize();
|
||||
NzPixelFormat::Uninitialize();
|
||||
NzFont::Uninitialize();
|
||||
NzBuffer::Uninitialize();
|
||||
|
||||
NazaraNotice("Uninitialized: Utility module");
|
||||
@@ -146,7 +159,7 @@ unsigned int NzUtility::ComponentCount[nzComponentType_Max+1] =
|
||||
|
||||
static_assert(nzComponentType_Max+1 == 14, "Component count array is incomplete");
|
||||
|
||||
unsigned int NzUtility::ComponentStride[nzComponentType_Max+1] =
|
||||
std::size_t NzUtility::ComponentStride[nzComponentType_Max+1] =
|
||||
{
|
||||
4*sizeof(nzUInt8), // nzComponentType_Color
|
||||
1*sizeof(double), // nzComponentType_Double1
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/VertexBuffer.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzVertexBuffer::NzVertexBuffer(const NzVertexDeclaration* vertexDeclaration, NzBuffer* buffer)
|
||||
{
|
||||
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, nzBufferStorage storage, nzBufferUsage usage)
|
||||
NzVertexBuffer::NzVertexBuffer(const NzVertexDeclaration* vertexDeclaration, unsigned int length, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
NzErrorFlags(nzErrorFlag_ThrowException, true);
|
||||
Reset(vertexDeclaration, length, storage, usage);
|
||||
}
|
||||
|
||||
@@ -27,15 +36,6 @@ m_vertexCount(vertexBuffer.m_vertexCount)
|
||||
{
|
||||
}
|
||||
|
||||
NzVertexBuffer::NzVertexBuffer(NzVertexBuffer&& vertexBuffer) noexcept :
|
||||
m_buffer(std::move(vertexBuffer.m_buffer)),
|
||||
m_vertexDeclaration(std::move(vertexBuffer.m_vertexDeclaration)),
|
||||
m_endOffset(vertexBuffer.m_endOffset),
|
||||
m_startOffset(vertexBuffer.m_startOffset),
|
||||
m_vertexCount(vertexBuffer.m_vertexCount)
|
||||
{
|
||||
}
|
||||
|
||||
NzVertexBuffer::~NzVertexBuffer()
|
||||
{
|
||||
NotifyDestroy();
|
||||
@@ -50,6 +50,12 @@ bool NzVertexBuffer::Fill(const void* data, unsigned int startVertex, unsigned i
|
||||
bool NzVertexBuffer::FillRaw(const void* data, unsigned int offset, unsigned int size, bool forceDiscard)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_buffer)
|
||||
{
|
||||
NazaraError("No buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_startOffset + offset + size > m_endOffset)
|
||||
{
|
||||
NazaraError("Exceeding virtual buffer size");
|
||||
@@ -118,6 +124,12 @@ void* NzVertexBuffer::Map(nzBufferAccess access, unsigned int startVertex, unsig
|
||||
void* NzVertexBuffer::Map(nzBufferAccess 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");
|
||||
@@ -168,6 +180,11 @@ void NzVertexBuffer::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
|
||||
@@ -177,9 +194,9 @@ void NzVertexBuffer::Reset(const NzVertexDeclaration* vertexDeclaration, NzBuffe
|
||||
return;
|
||||
}
|
||||
|
||||
if (endOffset > startOffset)
|
||||
if (startOffset > endOffset)
|
||||
{
|
||||
NazaraError("End offset cannot be over start offset");
|
||||
NazaraError("Start offset cannot be over end offset");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -204,7 +221,7 @@ void NzVertexBuffer::Reset(const NzVertexDeclaration* vertexDeclaration, NzBuffe
|
||||
m_vertexDeclaration = vertexDeclaration;
|
||||
}
|
||||
|
||||
void NzVertexBuffer::Reset(const NzVertexDeclaration* vertexDeclaration, unsigned int length, nzBufferStorage storage, nzBufferUsage usage)
|
||||
void NzVertexBuffer::Reset(const NzVertexDeclaration* vertexDeclaration, unsigned int length, nzUInt32 storage, nzBufferUsage usage)
|
||||
{
|
||||
m_endOffset = length * ((vertexDeclaration) ? vertexDeclaration->GetStride() : 1);
|
||||
m_startOffset = 0;
|
||||
@@ -224,16 +241,7 @@ void NzVertexBuffer::Reset(const NzVertexBuffer& vertexBuffer)
|
||||
m_vertexDeclaration = vertexBuffer.m_vertexDeclaration;
|
||||
}
|
||||
|
||||
void NzVertexBuffer::Reset(NzVertexBuffer&& vertexBuffer) noexcept
|
||||
{
|
||||
m_buffer = std::move(vertexBuffer.m_buffer);
|
||||
m_endOffset = vertexBuffer.m_endOffset;
|
||||
m_startOffset = vertexBuffer.m_startOffset;
|
||||
m_vertexCount = vertexBuffer.m_vertexCount;
|
||||
m_vertexDeclaration = std::move(vertexBuffer.m_vertexDeclaration);
|
||||
}
|
||||
|
||||
bool NzVertexBuffer::SetStorage(nzBufferStorage storage)
|
||||
bool NzVertexBuffer::SetStorage(nzUInt32 storage)
|
||||
{
|
||||
return m_buffer->SetStorage(storage);
|
||||
}
|
||||
@@ -263,10 +271,3 @@ NzVertexBuffer& NzVertexBuffer::operator=(const NzVertexBuffer& vertexBuffer)
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NzVertexBuffer& NzVertexBuffer::operator=(NzVertexBuffer&& vertexBuffer) noexcept
|
||||
{
|
||||
Reset(vertexBuffer);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -192,6 +192,13 @@ bool NzVertexDeclaration::Initialize()
|
||||
|
||||
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));
|
||||
@@ -205,6 +212,21 @@ bool NzVertexDeclaration::Initialize()
|
||||
|
||||
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));
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/VertexMapper.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Utility/BufferMapper.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <Nazara/Utility/SubMesh.hpp>
|
||||
@@ -13,7 +12,8 @@
|
||||
|
||||
NzVertexMapper::NzVertexMapper(NzSubMesh* subMesh)
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowException);
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
|
||||
|
||||
NzVertexBuffer* buffer = nullptr;
|
||||
switch (subMesh->GetAnimationType())
|
||||
{
|
||||
@@ -37,12 +37,19 @@ NzVertexMapper::NzVertexMapper(NzSubMesh* subMesh)
|
||||
NazaraInternalError("Animation type not handled (0x" + NzString::Number(subMesh->GetAnimationType(), 16) + ')');
|
||||
}
|
||||
|
||||
m_declaration = buffer->GetVertexDeclaration();
|
||||
m_vertexCount = subMesh->GetVertexCount();
|
||||
|
||||
m_mapper.Map(buffer, nzBufferAccess_ReadWrite);
|
||||
}
|
||||
|
||||
NzVertexMapper::NzVertexMapper(NzVertexBuffer* vertexBuffer, unsigned int vertexCount)
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
|
||||
|
||||
m_mapper.Map(vertexBuffer, nzBufferAccess_ReadWrite);
|
||||
m_vertexCount = vertexCount;
|
||||
}
|
||||
|
||||
NzVertexMapper::~NzVertexMapper() = default;
|
||||
|
||||
unsigned int NzVertexMapper::GetVertexCount() const
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/VideoMode.hpp>
|
||||
#include <Nazara/Utility/VideoModeImpl.hpp>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
NzVideoMode::NzVideoMode() :
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -222,7 +222,7 @@ NzVector2i NzEventImpl::GetMousePosition(const NzWindow& relativeTo)
|
||||
}
|
||||
else
|
||||
{
|
||||
NazaraError("Window's handle is invalid");
|
||||
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);
|
||||
@@ -289,5 +289,5 @@ void NzEventImpl::SetMousePosition(int x, int y, const NzWindow& relativeTo)
|
||||
SetCursorPos(pos.x, pos.y);
|
||||
}
|
||||
else
|
||||
NazaraError("Window's handle is invalid");
|
||||
NazaraError("Invalid window handle");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -79,7 +79,7 @@ m_scrolling(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool NzWindowImpl::Create(NzVideoMode mode, const NzString& title, nzUInt32 style)
|
||||
bool NzWindowImpl::Create(const NzVideoMode& mode, const NzString& title, nzUInt32 style)
|
||||
{
|
||||
bool fullscreen = (style & nzWindowStyle_Fullscreen) != 0;
|
||||
DWORD win32Style, win32StyleEx;
|
||||
@@ -183,7 +183,7 @@ bool NzWindowImpl::Create(NzVideoMode mode, const NzString& title, nzUInt32 styl
|
||||
GetWindowRect(m_handle, &windowRect);
|
||||
|
||||
m_position.Set(windowRect.left, windowRect.top);
|
||||
m_size.Set(clientRect.right-clientRect.left, clientRect.bottom-clientRect.top);
|
||||
m_size.Set(clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
@@ -34,7 +34,7 @@ class NzWindowImpl : NzNonCopyable
|
||||
NzWindowImpl(NzWindow* parent);
|
||||
~NzWindowImpl() = default;
|
||||
|
||||
bool Create(NzVideoMode mode, const NzString& title, nzUInt32 style);
|
||||
bool Create(const NzVideoMode& mode, const NzString& title, nzUInt32 style);
|
||||
bool Create(NzWindowHandle handle);
|
||||
|
||||
void Destroy();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Utility module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Utility/Window.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Core/LockGuard.hpp>
|
||||
#include <Nazara/Utility/Cursor.hpp>
|
||||
#include <Nazara/Utility/Image.hpp>
|
||||
@@ -45,15 +46,8 @@ m_waitForEvent(false)
|
||||
m_impl(nullptr)
|
||||
#endif
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
|
||||
Create(mode, title, style);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Failed to create window");
|
||||
throw std::runtime_error("Constructor failed");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NzWindow::NzWindow(NzWindowHandle handle) :
|
||||
@@ -65,15 +59,8 @@ m_waitForEvent(false)
|
||||
m_impl(nullptr)
|
||||
#endif
|
||||
{
|
||||
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
|
||||
Create(handle);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Failed to create window");
|
||||
throw std::runtime_error("Constructor failed");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NzWindow::~NzWindow()
|
||||
|
||||
Reference in New Issue
Block a user