Merge branch 'master' into vulkan
This commit is contained in:
@@ -61,8 +61,6 @@ namespace Nz
|
||||
* \return true if creation was succesful
|
||||
*
|
||||
* \param soundStream Sound stream which is the source for the music
|
||||
*
|
||||
* \remark Produces a NazaraError if soundStream is invalid with NAZARA_AUDIO_SAFE defined
|
||||
*/
|
||||
|
||||
bool Music::Create(SoundStream* soundStream)
|
||||
@@ -86,13 +84,14 @@ namespace Nz
|
||||
|
||||
/*!
|
||||
* \brief Destroys the current music and frees resources
|
||||
*
|
||||
* \remark If the Music is playing, it is stopped first.
|
||||
*/
|
||||
|
||||
void Music::Destroy()
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
Stop();
|
||||
StopThread();
|
||||
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
@@ -104,18 +103,11 @@ namespace Nz
|
||||
*
|
||||
* \param loop Should music loop
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
|
||||
void Music::EnableLooping(bool loop)
|
||||
{
|
||||
#if NAZARA_AUDIO_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Music not created");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
NazaraAssert(m_impl, "Music not created");
|
||||
|
||||
m_impl->loop = loop;
|
||||
}
|
||||
@@ -124,18 +116,11 @@ namespace Nz
|
||||
* \brief Gets the duration of the music
|
||||
* \return Duration of the music in milliseconds
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
|
||||
UInt32 Music::GetDuration() const
|
||||
{
|
||||
#if NAZARA_AUDIO_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Music not created");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
NazaraAssert(m_impl, "Music not created");
|
||||
|
||||
return m_impl->stream->GetDuration();
|
||||
}
|
||||
@@ -144,18 +129,11 @@ namespace Nz
|
||||
* \brief Gets the format of the music
|
||||
* \return Enumeration of type AudioFormat (mono, stereo, ...)
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
|
||||
AudioFormat Music::GetFormat() const
|
||||
{
|
||||
#if NAZARA_AUDIO_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Music not created");
|
||||
return AudioFormat_Unknown;
|
||||
}
|
||||
#endif
|
||||
NazaraAssert(m_impl, "Music not created");
|
||||
|
||||
return m_impl->stream->GetFormat();
|
||||
}
|
||||
@@ -164,7 +142,7 @@ namespace Nz
|
||||
* \brief Gets the current offset in the music
|
||||
* \return Offset in milliseconds (works with entire seconds)
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
UInt32 Music::GetPlayingOffset() const
|
||||
{
|
||||
@@ -183,7 +161,7 @@ namespace Nz
|
||||
* \brief Gets the number of samples in the music
|
||||
* \return Count of samples (number of seconds * sample rate * channel count)
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
UInt64 Music::GetSampleCount() const
|
||||
{
|
||||
@@ -196,7 +174,7 @@ namespace Nz
|
||||
* \brief Gets the rates of sample in the music
|
||||
* \return Rate of sample in Hertz (Hz)
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
UInt32 Music::GetSampleRate() const
|
||||
{
|
||||
@@ -209,8 +187,7 @@ namespace Nz
|
||||
* \brief Gets the status of the music
|
||||
* \return Enumeration of type SoundStatus (Playing, Stopped, ...)
|
||||
*
|
||||
* \remark If the music is not playing, Stopped is returned
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
SoundStatus Music::GetStatus() const
|
||||
{
|
||||
@@ -229,44 +206,37 @@ namespace Nz
|
||||
* \brief Checks whether the music is looping
|
||||
* \return true if it is the case
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
|
||||
bool Music::IsLooping() const
|
||||
{
|
||||
#if NAZARA_AUDIO_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Music not created");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
NazaraAssert(m_impl, "Music not created");
|
||||
|
||||
return m_impl->loop;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Loads the music from file
|
||||
* \return true if loading is successful
|
||||
* \brief Opens the music from a file
|
||||
* \return true if the file was successfully opened
|
||||
*
|
||||
* \param filePath Path to the file
|
||||
* \param params Parameters for the music
|
||||
*/
|
||||
|
||||
bool Music::OpenFromFile(const String& filePath, const MusicParams& params)
|
||||
{
|
||||
return MusicLoader::LoadFromFile(this, filePath, params);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Loads the music from memory
|
||||
* \brief Opens the music from memory
|
||||
* \return true if loading is successful
|
||||
*
|
||||
* \param data Raw memory
|
||||
* \param size Size of the memory
|
||||
* \param params Parameters for the music
|
||||
*
|
||||
* \remark The memory pointer must stay valid (accessible) as long as the music is playing
|
||||
*/
|
||||
|
||||
bool Music::OpenFromMemory(const void* data, std::size_t size, const MusicParams& params)
|
||||
{
|
||||
return MusicLoader::LoadFromMemory(this, data, size, params);
|
||||
@@ -278,8 +248,9 @@ namespace Nz
|
||||
*
|
||||
* \param stream Stream to the music
|
||||
* \param params Parameters for the music
|
||||
*
|
||||
* \remark The stream must stay valid as long as the music is playing
|
||||
*/
|
||||
|
||||
bool Music::OpenFromStream(Stream& stream, const MusicParams& params)
|
||||
{
|
||||
return MusicLoader::LoadFromStream(this, stream, params);
|
||||
@@ -287,8 +258,9 @@ namespace Nz
|
||||
|
||||
/*!
|
||||
* \brief Pauses the music
|
||||
*
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
|
||||
void Music::Pause()
|
||||
{
|
||||
alSourcePause(m_source);
|
||||
@@ -297,18 +269,16 @@ namespace Nz
|
||||
/*!
|
||||
* \brief Plays the music
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* Plays/Resume the music.
|
||||
* If the music is currently playing, resets the playing offset to the beginning offset.
|
||||
* If the music is currently paused, resumes the playing.
|
||||
* If the music is currently stopped, starts the playing at the previously set playing offset.
|
||||
*
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
|
||||
void Music::Play()
|
||||
{
|
||||
#if NAZARA_AUDIO_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Music not created");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
NazaraAssert(m_impl, "Music not created");
|
||||
|
||||
// Maybe we are already playing
|
||||
if (m_impl->streaming)
|
||||
@@ -336,25 +306,20 @@ namespace Nz
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the playing offset for the music
|
||||
* \brief Changes the playing offset of the music
|
||||
*
|
||||
* \param offset Offset in the music in milliseconds
|
||||
* If the music is not playing, this sets the playing offset for the next Play call
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \param offset The offset in milliseconds
|
||||
*
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
|
||||
void Music::SetPlayingOffset(UInt32 offset)
|
||||
{
|
||||
#if NAZARA_AUDIO_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Music not created");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
NazaraAssert(m_impl, "Music not created");
|
||||
|
||||
bool isPlaying = m_impl->streaming;
|
||||
|
||||
|
||||
if (isPlaying)
|
||||
Stop();
|
||||
|
||||
@@ -368,33 +333,16 @@ namespace Nz
|
||||
/*!
|
||||
* \brief Stops the music
|
||||
*
|
||||
* \remark Produces a NazaraError if there is no music with NAZARA_AUDIO_SAFE defined
|
||||
* \remark Music must be valid when calling this function
|
||||
*/
|
||||
|
||||
void Music::Stop()
|
||||
{
|
||||
#if NAZARA_AUDIO_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Music not created");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
NazaraAssert(m_impl, "Music not created");
|
||||
|
||||
if (m_impl->streaming)
|
||||
{
|
||||
m_impl->streaming = false;
|
||||
m_impl->thread.Join();
|
||||
}
|
||||
StopThread();
|
||||
SetPlayingOffset(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Fills the buffer and queues it up
|
||||
* \return true if operation was successful
|
||||
*
|
||||
* \param buffer Index of the buffer
|
||||
*/
|
||||
|
||||
bool Music::FillAndQueueBuffer(unsigned int buffer)
|
||||
{
|
||||
std::size_t sampleCount = m_impl->chunkSamples.size();
|
||||
@@ -425,10 +373,6 @@ namespace Nz
|
||||
return sampleRead != sampleCount; // End of stream (Does not happen when looping)
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Thread function for the music
|
||||
*/
|
||||
|
||||
void Music::MusicThread()
|
||||
{
|
||||
// Allocation of streaming buffers
|
||||
@@ -463,11 +407,11 @@ namespace Nz
|
||||
{
|
||||
ALuint buffer;
|
||||
alSourceUnqueueBuffers(m_source, 1, &buffer);
|
||||
|
||||
|
||||
ALint bits, size;
|
||||
alGetBufferi(buffer, AL_BITS, &bits);
|
||||
alGetBufferi(buffer, AL_SIZE, &size);
|
||||
|
||||
|
||||
if (bits != 0)
|
||||
m_impl->processedSamples += (8 * size) / bits;
|
||||
|
||||
@@ -495,5 +439,14 @@ namespace Nz
|
||||
alDeleteBuffers(NAZARA_AUDIO_STREAMED_BUFFER_COUNT, buffers);
|
||||
}
|
||||
|
||||
void Music::StopThread()
|
||||
{
|
||||
if (m_impl->streaming)
|
||||
{
|
||||
m_impl->streaming = false;
|
||||
m_impl->thread.Join();
|
||||
}
|
||||
}
|
||||
|
||||
MusicLoader::LoaderList Music::s_loaders;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Nz
|
||||
* \param openMode Reading/writing mode for the stream
|
||||
*/
|
||||
|
||||
ByteStream::ByteStream(ByteArray* byteArray, UInt32 openMode) :
|
||||
ByteStream::ByteStream(ByteArray* byteArray, OpenModeFlags openMode) :
|
||||
ByteStream()
|
||||
{
|
||||
SetStream(byteArray, openMode);
|
||||
@@ -67,7 +67,7 @@ namespace Nz
|
||||
* \param openMode Reading/writing mode for the stream
|
||||
*/
|
||||
|
||||
void ByteStream::SetStream(ByteArray* byteArray, UInt32 openMode)
|
||||
void ByteStream::SetStream(ByteArray* byteArray, OpenModeFlags openMode)
|
||||
{
|
||||
std::unique_ptr<Stream> stream(new MemoryStream(byteArray, openMode));
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Nz
|
||||
* \param openMode Flag of the file
|
||||
*/
|
||||
|
||||
File::File(const String& filePath, UInt32 openMode) :
|
||||
File::File(const String& filePath, OpenModeFlags openMode) :
|
||||
File()
|
||||
{
|
||||
Open(filePath, openMode);
|
||||
@@ -311,7 +311,7 @@ namespace Nz
|
||||
* \remark Produces a NazaraError if OS error to open a file
|
||||
*/
|
||||
|
||||
bool File::Open(unsigned int openMode)
|
||||
bool File::Open(OpenModeFlags openMode)
|
||||
{
|
||||
NazaraLock(m_mutex)
|
||||
|
||||
@@ -352,7 +352,7 @@ namespace Nz
|
||||
* \remark Produces a NazaraError if OS error to open a file
|
||||
*/
|
||||
|
||||
bool File::Open(const String& filePath, unsigned int openMode)
|
||||
bool File::Open(const String& filePath, OpenModeFlags openMode)
|
||||
{
|
||||
NazaraLock(m_mutex)
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Nz
|
||||
* \remark Produces a NazaraAssert if byteArray is nullptr
|
||||
*/
|
||||
|
||||
void MemoryStream::SetBuffer(ByteArray* byteArray, UInt32 openMode)
|
||||
void MemoryStream::SetBuffer(ByteArray* byteArray, OpenModeFlags openMode)
|
||||
{
|
||||
NazaraAssert(byteArray, "Invalid ByteArray");
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Nz
|
||||
return static_cast<UInt64>(position);
|
||||
}
|
||||
|
||||
bool FileImpl::Open(const String& filePath, UInt32 mode)
|
||||
bool FileImpl::Open(const String& filePath, OpenModeFlags mode)
|
||||
{
|
||||
int flags;
|
||||
mode_t permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
@@ -66,6 +66,9 @@ namespace Nz
|
||||
if (mode & OpenMode_Append)
|
||||
flags |= O_APPEND;
|
||||
|
||||
if (mode & OpenMode_MustExist)
|
||||
flags &= ~O_CREAT;
|
||||
|
||||
if (mode & OpenMode_Truncate)
|
||||
flags |= O_TRUNC;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Nz
|
||||
bool EndOfFile() const;
|
||||
void Flush();
|
||||
UInt64 GetCursorPos() const;
|
||||
bool Open(const String& filePath, UInt32 mode);
|
||||
bool Open(const String& filePath, OpenModeFlags mode);
|
||||
std::size_t Read(void* buffer, std::size_t size);
|
||||
bool SetCursorPos(CursorPosition pos, Int64 offset);
|
||||
bool SetSize(UInt64 size);
|
||||
|
||||
@@ -2094,6 +2094,43 @@ namespace Nz
|
||||
return m_sharedString->capacity;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Gets the index where a character begin
|
||||
*
|
||||
* Iterate through the string to find the starting position of a specific character index
|
||||
* This is useful because non-ASCII characters may be encoded using multiple bytes.
|
||||
*
|
||||
* \param characterIndex Index of the character to search for
|
||||
*
|
||||
* \return Starting index
|
||||
*/
|
||||
std::size_t String::GetCharacterPosition(std::size_t characterIndex) const
|
||||
{
|
||||
const char* ptr = m_sharedString->string.get();
|
||||
const char* end = &m_sharedString->string[m_sharedString->size];
|
||||
|
||||
try
|
||||
{
|
||||
utf8::advance(ptr, characterIndex, end);
|
||||
|
||||
return ptr - m_sharedString->string.get();
|
||||
}
|
||||
catch (utf8::not_enough_room& e)
|
||||
{
|
||||
// Returns npos
|
||||
}
|
||||
catch (utf8::exception& e)
|
||||
{
|
||||
NazaraError("UTF-8 error: " + String(e.what()));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
NazaraError(e.what());
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Gets the raw buffer
|
||||
* \return Raw buffer
|
||||
@@ -5948,7 +5985,7 @@ namespace std
|
||||
|
||||
char c;
|
||||
|
||||
for (;;)
|
||||
for (;;)
|
||||
{
|
||||
is.get(c);
|
||||
if (c != delim && c != '\0')
|
||||
|
||||
@@ -55,20 +55,20 @@ namespace Nz
|
||||
return position.QuadPart;
|
||||
}
|
||||
|
||||
bool FileImpl::Open(const String& filePath, UInt32 mode)
|
||||
bool FileImpl::Open(const String& filePath, OpenModeFlags mode)
|
||||
{
|
||||
DWORD access = 0;
|
||||
DWORD shareMode = FILE_SHARE_READ;
|
||||
DWORD openMode = 0;
|
||||
|
||||
|
||||
if (mode & OpenMode_ReadOnly)
|
||||
{
|
||||
access |= GENERIC_READ;
|
||||
|
||||
if (mode & OpenMode_MustExit || (mode & OpenMode_WriteOnly) == 0)
|
||||
if (mode & OpenMode_MustExist || (mode & OpenMode_WriteOnly) == 0)
|
||||
openMode |= OPEN_EXISTING;
|
||||
}
|
||||
|
||||
|
||||
if (mode & OpenMode_WriteOnly)
|
||||
{
|
||||
if (mode & OpenMode_Append)
|
||||
@@ -78,7 +78,7 @@ namespace Nz
|
||||
|
||||
if (mode & OpenMode_Truncate)
|
||||
openMode |= CREATE_ALWAYS;
|
||||
else if (mode & OpenMode_MustExit)
|
||||
else if (mode & OpenMode_MustExist)
|
||||
openMode |= OPEN_EXISTING;
|
||||
else
|
||||
openMode |= OPEN_ALWAYS;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Nz
|
||||
bool EndOfFile() const;
|
||||
void Flush();
|
||||
UInt64 GetCursorPos() const;
|
||||
bool Open(const String& filePath, UInt32 mode);
|
||||
bool Open(const String& filePath, OpenModeFlags mode);
|
||||
std::size_t Read(void* buffer, std::size_t size);
|
||||
bool SetCursorPos(CursorPosition pos, Int64 offset);
|
||||
bool SetSize(UInt64 size);
|
||||
|
||||
@@ -527,6 +527,24 @@ namespace Nz
|
||||
layers.erase(it++);
|
||||
else
|
||||
{
|
||||
for (auto& pipelinePair : layer.billboards)
|
||||
{
|
||||
auto& pipelineEntry = pipelinePair.second;
|
||||
|
||||
if (pipelineEntry.enabled)
|
||||
{
|
||||
for (auto& matIt : pipelinePair.second.materialMap)
|
||||
{
|
||||
auto& entry = matIt.second;
|
||||
auto& billboardVector = entry.billboards;
|
||||
|
||||
billboardVector.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pipelineEntry.enabled = false;
|
||||
}
|
||||
|
||||
for (auto& pipelinePair : layer.basicSprites)
|
||||
{
|
||||
auto& pipelineEntry = pipelinePair.second;
|
||||
|
||||
@@ -561,8 +561,6 @@ namespace Nz
|
||||
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, renderedBillboardCount * 6);
|
||||
}
|
||||
while (billboardCount > 0);
|
||||
|
||||
billboardVector.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -772,7 +770,6 @@ namespace Nz
|
||||
}
|
||||
}
|
||||
}
|
||||
instances.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +108,8 @@ namespace Nz
|
||||
for (std::size_t i = 0; i < glyphCount; ++i)
|
||||
{
|
||||
const AbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i);
|
||||
if (!glyph.atlas)
|
||||
continue;
|
||||
|
||||
Texture* texture = static_cast<Texture*>(glyph.atlas);
|
||||
if (lastTexture != texture)
|
||||
@@ -149,6 +151,8 @@ namespace Nz
|
||||
for (unsigned int i = 0; i < glyphCount; ++i)
|
||||
{
|
||||
const AbstractTextDrawer::Glyph& glyph = drawer.GetGlyph(i);
|
||||
if (!glyph.atlas)
|
||||
continue;
|
||||
|
||||
Texture* texture = static_cast<Texture*>(glyph.atlas);
|
||||
if (lastTexture != texture)
|
||||
@@ -200,8 +204,8 @@ namespace Nz
|
||||
void TextSprite::MakeBoundingVolume() const
|
||||
{
|
||||
Rectf bounds(m_localBounds);
|
||||
Vector2f max = bounds.GetMaximum();
|
||||
Vector2f min = bounds.GetMinimum();
|
||||
Vector2f max = m_scale * bounds.GetMaximum();
|
||||
Vector2f min = m_scale * bounds.GetMinimum();
|
||||
|
||||
m_boundingVolume.Set(min.x * Vector3f::Right() + min.y * Vector3f::Down(), max.x * Vector3f::Right() + max.y * Vector3f::Down());
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Nz
|
||||
* \remark Produces a NazaraAssert if cursor position is greather than the capacity
|
||||
*/
|
||||
|
||||
void NetPacket::InitStream(std::size_t minCapacity, UInt64 cursorPos, UInt32 openMode)
|
||||
void NetPacket::InitStream(std::size_t minCapacity, UInt64 cursorPos, OpenModeFlags openMode)
|
||||
{
|
||||
NazaraAssert(minCapacity >= cursorPos, "Cannot init stream with a smaller capacity than wanted cursor pos");
|
||||
|
||||
|
||||
@@ -1,855 +0,0 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/RenderTexture.hpp>
|
||||
#include <Nazara/Core/CallOnExit.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Renderer/Context.hpp>
|
||||
#include <Nazara/Renderer/OpenGL.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
#include <Nazara/Renderer/RenderBuffer.hpp>
|
||||
#include <Nazara/Renderer/Texture.hpp>
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct Attachment
|
||||
{
|
||||
NazaraSlot(RenderBuffer, OnRenderBufferDestroy, renderBufferDestroySlot);
|
||||
NazaraSlot(Texture, OnTextureDestroy, textureDestroySlot);
|
||||
|
||||
RenderBufferRef buffer;
|
||||
TextureRef texture;
|
||||
|
||||
AttachmentPoint attachmentPoint;
|
||||
bool isBuffer;
|
||||
bool isUsed = false;
|
||||
unsigned int height;
|
||||
unsigned int width;
|
||||
};
|
||||
|
||||
unsigned int attachmentIndex[AttachmentPoint_Max+1] =
|
||||
{
|
||||
3, // AttachmentPoint_Color
|
||||
0, // AttachmentPoint_Depth
|
||||
1, // AttachmentPoint_DepthStencil
|
||||
2 // AttachmentPoint_Stencil
|
||||
};
|
||||
|
||||
AttachmentPoint FormatTypeToAttachment(PixelFormatType format)
|
||||
{
|
||||
const PixelFormatInfo& info = PixelFormat::GetInfo(format);
|
||||
switch (info.content)
|
||||
{
|
||||
case PixelFormatContent_ColorRGBA:
|
||||
return AttachmentPoint_Color;
|
||||
|
||||
case PixelFormatContent_DepthStencil:
|
||||
return (!info.greenMask.TestAny()) ? AttachmentPoint_Depth : AttachmentPoint_DepthStencil;
|
||||
|
||||
case PixelFormatContent_Stencil:
|
||||
return AttachmentPoint_Stencil;
|
||||
|
||||
case PixelFormatContent_Undefined:
|
||||
break;
|
||||
}
|
||||
|
||||
NazaraInternalError("Unexpected pixel format content: 0x" + String::Number(info.content, 16));
|
||||
return AttachmentPoint_Max;
|
||||
}
|
||||
|
||||
GLuint lockedPrevious = 0;
|
||||
UInt8 lockedLevel = 0;
|
||||
}
|
||||
|
||||
struct RenderTextureImpl
|
||||
{
|
||||
NazaraSlot(Context, OnContextDestroy, contextDestroySlot);
|
||||
|
||||
GLuint fbo;
|
||||
std::vector<Attachment> attachments;
|
||||
std::vector<UInt8> colorTargets;
|
||||
mutable std::vector<GLenum> drawBuffers;
|
||||
const Context* context;
|
||||
bool complete = false;
|
||||
bool userDefinedTargets = false;
|
||||
unsigned int height;
|
||||
unsigned int width;
|
||||
};
|
||||
|
||||
bool RenderTexture::AttachBuffer(AttachmentPoint attachmentPoint, UInt8 index, RenderBuffer* buffer)
|
||||
{
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Render texture not created");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attachmentPoint != AttachmentPoint_Color)
|
||||
{
|
||||
if (index > 0)
|
||||
{
|
||||
NazaraError("Index must be 0 for non-color attachments");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index >= Renderer::GetMaxColorAttachments())
|
||||
{
|
||||
NazaraError("Color index is over max color attachments (" + String::Number(index) + " >= " + String::Number(Renderer::GetMaxColorAttachments()) + ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer || !buffer->IsValid())
|
||||
{
|
||||
NazaraError("Invalid render buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int depthStencilIndex = attachmentIndex[AttachmentPoint_DepthStencil];
|
||||
if (m_impl->attachments.size() > depthStencilIndex && m_impl->attachments[depthStencilIndex].isUsed)
|
||||
{
|
||||
if (attachmentPoint == AttachmentPoint_Depth)
|
||||
{
|
||||
NazaraError("Depth target already attached by DepthStencil attachment");
|
||||
return false;
|
||||
}
|
||||
else if (attachmentPoint == AttachmentPoint_Stencil)
|
||||
{
|
||||
NazaraError("Stencil target already attached by DepthStencil attachment");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AttachmentPoint targetAttachmentPoint = FormatTypeToAttachment(buffer->GetFormat());
|
||||
if (targetAttachmentPoint != attachmentPoint && targetAttachmentPoint != AttachmentPoint_DepthStencil &&
|
||||
attachmentPoint != AttachmentPoint_Depth && attachmentPoint != AttachmentPoint_Stencil)
|
||||
{
|
||||
NazaraError("Pixel format type does not match attachment point type");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!Lock())
|
||||
{
|
||||
NazaraError("Failed to lock render texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Détachement de l'attache précédente (Si il y a)
|
||||
Detach(attachmentPoint, index);
|
||||
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, OpenGL::Attachment[attachmentPoint]+index, GL_RENDERBUFFER, buffer->GetOpenGLID());
|
||||
|
||||
Unlock();
|
||||
|
||||
unsigned int attachIndex = attachmentIndex[attachmentPoint] + index;
|
||||
if (attachIndex >= m_impl->attachments.size())
|
||||
m_impl->attachments.resize(attachIndex+1);
|
||||
|
||||
Attachment& attachment = m_impl->attachments[attachIndex];
|
||||
attachment.attachmentPoint = attachmentPoint;
|
||||
attachment.buffer = buffer;
|
||||
attachment.renderBufferDestroySlot.Connect(buffer->OnRenderBufferDestroy, std::bind(&RenderTexture::OnRenderBufferDestroy, this, std::placeholders::_1, attachIndex));
|
||||
attachment.isBuffer = true;
|
||||
attachment.isUsed = true;
|
||||
attachment.height = buffer->GetHeight();
|
||||
attachment.width = buffer->GetWidth();
|
||||
|
||||
InvalidateSize();
|
||||
InvalidateTargets();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderTexture::AttachBuffer(AttachmentPoint attachmentPoint, UInt8 index, PixelFormatType format, unsigned int width, unsigned int height)
|
||||
{
|
||||
RenderBufferRef renderBuffer = RenderBuffer::New();
|
||||
if (!renderBuffer->Create(format, width, height))
|
||||
{
|
||||
NazaraError("Failed to create RenderBuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AttachBuffer(attachmentPoint, index, renderBuffer))
|
||||
{
|
||||
NazaraError("Failed to attach buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderTexture::AttachTexture(AttachmentPoint attachmentPoint, UInt8 index, Texture* texture, unsigned int z)
|
||||
{
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Render texture not created");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attachmentPoint != AttachmentPoint_Color)
|
||||
{
|
||||
if (index > 0)
|
||||
{
|
||||
NazaraError("Index must be 0 for non-color attachments");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index >= Renderer::GetMaxColorAttachments())
|
||||
{
|
||||
NazaraError("Color index is over max color attachments (" + String::Number(index) + " >= " + String::Number(Renderer::GetMaxColorAttachments()) + ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (attachmentPoint == AttachmentPoint_Stencil)
|
||||
{
|
||||
NazaraError("Targeting stencil-only textures is not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int depthStencilIndex = attachmentIndex[AttachmentPoint_DepthStencil];
|
||||
if (attachmentPoint == AttachmentPoint_Depth && m_impl->attachments.size() > depthStencilIndex &&
|
||||
m_impl->attachments[depthStencilIndex].isUsed)
|
||||
{
|
||||
NazaraError("Depth target already attached by DepthStencil attachment");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!texture || !texture->IsValid())
|
||||
{
|
||||
NazaraError("Invalid texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int depth = (texture->GetType() == ImageType_Cubemap) ? 6 : texture->GetDepth();
|
||||
if (z >= depth)
|
||||
{
|
||||
NazaraError("Z value exceeds depth (" + String::Number(z) + " >= (" + String::Number(depth) + ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
AttachmentPoint targetAttachmentPoint = FormatTypeToAttachment(texture->GetFormat());
|
||||
if (targetAttachmentPoint != attachmentPoint && targetAttachmentPoint != AttachmentPoint_DepthStencil &&
|
||||
attachmentPoint != AttachmentPoint_Depth && attachmentPoint != AttachmentPoint_Stencil)
|
||||
{
|
||||
NazaraError("Pixel format type does not match attachment point type");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!Lock())
|
||||
{
|
||||
NazaraError("Failed to lock render texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Détachement de l'attache précédente (Si il y a)
|
||||
Detach(attachmentPoint, index);
|
||||
|
||||
switch (texture->GetType())
|
||||
{
|
||||
case ImageType_1D:
|
||||
glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, OpenGL::Attachment[attachmentPoint]+index, GL_TEXTURE_1D, texture->GetOpenGLID(), 0);
|
||||
break;
|
||||
|
||||
case ImageType_1D_Array:
|
||||
case ImageType_2D_Array:
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, OpenGL::Attachment[attachmentPoint]+index, texture->GetOpenGLID(), 0, z);
|
||||
break;
|
||||
|
||||
case ImageType_2D:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, OpenGL::Attachment[attachmentPoint]+index, GL_TEXTURE_2D, texture->GetOpenGLID(), 0);
|
||||
break;
|
||||
|
||||
case ImageType_3D:
|
||||
glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, OpenGL::Attachment[attachmentPoint]+index, GL_TEXTURE_3D, texture->GetOpenGLID(), 0, z);
|
||||
break;
|
||||
|
||||
case ImageType_Cubemap:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, OpenGL::Attachment[attachmentPoint]+index, OpenGL::CubemapFace[z], texture->GetOpenGLID(), 0);
|
||||
break;
|
||||
}
|
||||
|
||||
Unlock();
|
||||
|
||||
unsigned int attachIndex = attachmentIndex[attachmentPoint] + index;
|
||||
if (attachIndex >= m_impl->attachments.size())
|
||||
m_impl->attachments.resize(attachIndex+1);
|
||||
|
||||
Attachment& attachment = m_impl->attachments[attachIndex];
|
||||
attachment.attachmentPoint = attachmentPoint;
|
||||
attachment.isBuffer = false;
|
||||
attachment.isUsed = true;
|
||||
attachment.height = texture->GetHeight();
|
||||
attachment.texture = texture;
|
||||
attachment.textureDestroySlot.Connect(texture->OnTextureDestroy, std::bind(&RenderTexture::OnTextureDestroy, this, std::placeholders::_1, attachIndex));
|
||||
attachment.width = texture->GetWidth();
|
||||
|
||||
InvalidateSize();
|
||||
InvalidateTargets();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderTexture::Create(bool lock)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (Context::GetCurrent() == nullptr)
|
||||
{
|
||||
NazaraError("No active context");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<RenderTextureImpl> impl(new RenderTextureImpl);
|
||||
|
||||
impl->fbo = 0;
|
||||
glGenFramebuffers(1, &impl->fbo);
|
||||
|
||||
if (!impl->fbo)
|
||||
{
|
||||
NazaraError("Failed to create framebuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_impl = impl.release();
|
||||
m_impl->context = Context::GetCurrent();
|
||||
m_impl->contextDestroySlot.Connect(m_impl->context->OnContextDestroy, this, &RenderTexture::OnContextDestroy);
|
||||
|
||||
m_checked = false;
|
||||
m_drawBuffersUpdated = true;
|
||||
m_sizeUpdated = false;
|
||||
m_targetsUpdated = true;
|
||||
|
||||
if (lock)
|
||||
{
|
||||
// En cas d'exception, la ressource sera quand même libérée
|
||||
CallOnExit onExit([this] ()
|
||||
{
|
||||
Destroy();
|
||||
});
|
||||
|
||||
if (!Lock())
|
||||
{
|
||||
NazaraError("Failed to lock render texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
onExit.Reset();
|
||||
}
|
||||
|
||||
OnRenderTargetParametersChange(this);
|
||||
OnRenderTargetSizeChange(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderTexture::Destroy()
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
if (IsActive())
|
||||
Renderer::SetTarget(nullptr);
|
||||
|
||||
// Le FBO devant être supprimé dans son contexte d'origine, nous déléguons sa suppression à la classe OpenGL
|
||||
// Celle-ci va libérer le FBO dès que possible (la prochaine fois que son contexte d'origine sera actif)
|
||||
OpenGL::DeleteFrameBuffer(m_impl->context, m_impl->fbo);
|
||||
|
||||
delete m_impl; // Enlève également une références sur les Texture/RenderBuffer
|
||||
m_impl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTexture::Detach(AttachmentPoint attachmentPoint, UInt8 index)
|
||||
{
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (!m_impl)
|
||||
{
|
||||
NazaraError("Render texture not created");
|
||||
return;
|
||||
}
|
||||
|
||||
if (attachmentPoint != AttachmentPoint_Color && index > 0)
|
||||
{
|
||||
NazaraError("Index must be 0 for non-color attachments");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int attachIndex = attachmentIndex[attachmentPoint] + index;
|
||||
if (attachIndex >= m_impl->attachments.size())
|
||||
return;
|
||||
|
||||
Attachment& attachement = m_impl->attachments[attachIndex];
|
||||
if (!attachement.isUsed)
|
||||
return;
|
||||
|
||||
if (!Lock())
|
||||
{
|
||||
NazaraError("Failed to lock render texture");
|
||||
return;
|
||||
}
|
||||
|
||||
attachement.isUsed = false;
|
||||
|
||||
if (attachement.isBuffer)
|
||||
{
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, OpenGL::Attachment[attachmentPoint]+index, GL_RENDERBUFFER, 0);
|
||||
|
||||
attachement.buffer = nullptr;
|
||||
attachement.renderBufferDestroySlot.Disconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (glFramebufferTexture)
|
||||
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, OpenGL::Attachment[attachmentPoint]+index, 0, 0);
|
||||
else
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, OpenGL::Attachment[attachmentPoint]+index, 0, 0, 0);
|
||||
|
||||
attachement.texture = nullptr;
|
||||
attachement.textureDestroySlot.Disconnect();
|
||||
}
|
||||
|
||||
InvalidateSize();
|
||||
|
||||
if (attachement.attachmentPoint == AttachmentPoint_Color)
|
||||
InvalidateTargets();
|
||||
|
||||
Unlock();
|
||||
|
||||
m_checked = false;
|
||||
}
|
||||
|
||||
unsigned int RenderTexture::GetHeight() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
if (!m_sizeUpdated)
|
||||
UpdateSize();
|
||||
|
||||
return m_impl->height;
|
||||
}
|
||||
|
||||
RenderTargetParameters RenderTexture::GetParameters() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
///TODO
|
||||
return RenderTargetParameters();
|
||||
}
|
||||
|
||||
Vector2ui RenderTexture::GetSize() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
if (!m_sizeUpdated)
|
||||
UpdateSize();
|
||||
|
||||
return Vector2ui(m_impl->width, m_impl->height);
|
||||
}
|
||||
|
||||
unsigned int RenderTexture::GetWidth() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
if (!m_sizeUpdated)
|
||||
UpdateSize();
|
||||
|
||||
return m_impl->width;
|
||||
}
|
||||
|
||||
bool RenderTexture::IsComplete() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
if (!m_checked)
|
||||
{
|
||||
if (!Lock())
|
||||
{
|
||||
NazaraError("Failed to lock render texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
|
||||
Unlock();
|
||||
|
||||
m_impl->complete = false;
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case GL_FRAMEBUFFER_COMPLETE:
|
||||
m_impl->complete = true;
|
||||
break;
|
||||
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||
NazaraError("Incomplete attachment");
|
||||
break;
|
||||
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
|
||||
NazaraInternalError("Incomplete draw buffer");
|
||||
break;
|
||||
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
|
||||
NazaraInternalError("Incomplete read buffer");
|
||||
break;
|
||||
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
||||
NazaraError("Incomplete missing attachment");
|
||||
break;
|
||||
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
|
||||
NazaraError("Incomplete multisample");
|
||||
break;
|
||||
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
|
||||
NazaraError("Incomplete layer targets");
|
||||
break;
|
||||
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED:
|
||||
NazaraError("Render texture has unsupported attachments");
|
||||
break;
|
||||
|
||||
default:
|
||||
NazaraInternalError("Unknown error");
|
||||
}
|
||||
|
||||
m_checked = true;
|
||||
}
|
||||
|
||||
return m_impl->complete;
|
||||
}
|
||||
|
||||
bool RenderTexture::IsRenderable() const
|
||||
{
|
||||
return IsComplete() && !m_impl->attachments.empty();
|
||||
}
|
||||
|
||||
bool RenderTexture::Lock() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (Context::GetCurrent() != m_impl->context)
|
||||
{
|
||||
NazaraError("RenderTexture cannot be used with this context");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lockedLevel++ == 0)
|
||||
{
|
||||
GLint previous;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous);
|
||||
|
||||
lockedPrevious = previous;
|
||||
|
||||
if (lockedPrevious != m_impl->fbo)
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_impl->fbo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderTexture::SetColorTargets(const UInt8* targets, unsigned int targetCount) const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
for (unsigned int i = 0; i < targetCount; ++i)
|
||||
{
|
||||
unsigned int index = attachmentIndex[AttachmentPoint_Color] + targets[i];
|
||||
if (index >= m_impl->attachments.size() || !m_impl->attachments[index].isUsed)
|
||||
{
|
||||
NazaraError("Target " + String::Number(targets[i]) + " not attached");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->colorTargets.resize(targetCount);
|
||||
std::memcpy(&m_impl->colorTargets[0], targets, targetCount*sizeof(UInt8));
|
||||
|
||||
m_impl->userDefinedTargets = true;
|
||||
InvalidateTargets();
|
||||
}
|
||||
|
||||
void RenderTexture::SetColorTargets(const std::initializer_list<UInt8>& targets) const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
for (UInt8 target : targets)
|
||||
{
|
||||
unsigned int index = attachmentIndex[AttachmentPoint_Color] + target;
|
||||
if (index >= m_impl->attachments.size() || !m_impl->attachments[index].isUsed)
|
||||
{
|
||||
NazaraError("Target " + String::Number(target) + " not attached");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->colorTargets.resize(targets.size());
|
||||
|
||||
UInt8* ptr = &m_impl->colorTargets[0];
|
||||
for (UInt8 index : targets)
|
||||
*ptr++ = index;
|
||||
|
||||
m_impl->userDefinedTargets = true;
|
||||
InvalidateTargets();
|
||||
}
|
||||
|
||||
void RenderTexture::Unlock() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (Context::GetCurrent() != m_impl->context)
|
||||
{
|
||||
NazaraError("RenderTexture cannot be used with this context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (lockedLevel == 0)
|
||||
{
|
||||
NazaraWarning("Unlock called on non-locked texture");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (--lockedLevel == 0 && lockedPrevious != m_impl->fbo) // Ici, il est important qu'un FBO soit débindé si l'ancien était 0
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, lockedPrevious);
|
||||
}
|
||||
|
||||
unsigned int RenderTexture::GetOpenGLID() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (Context::GetCurrent() != m_impl->context)
|
||||
{
|
||||
NazaraError("RenderTexture cannot be used with this context");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_impl->fbo;
|
||||
}
|
||||
|
||||
bool RenderTexture::HasContext() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderTexture::Blit(RenderTexture* src, Rectui srcRect, RenderTexture* dst, Rectui dstRect, UInt32 buffers, bool bilinearFilter)
|
||||
{
|
||||
NazaraAssert(src && src->IsValid(), "Invalid source render texture");
|
||||
NazaraAssert(dst && dst->IsValid(), "Invalid destination render texture");
|
||||
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (srcRect.x+srcRect.width > src->GetWidth() || srcRect.y+srcRect.height > src->GetHeight())
|
||||
{
|
||||
NazaraError("Source rectangle dimensions are out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dstRect.x+dstRect.width > dst->GetWidth() || dstRect.y+dstRect.height > dst->GetHeight())
|
||||
{
|
||||
NazaraError("Destination rectangle dimensions are out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bilinearFilter && (buffers & RendererBuffer_Depth || buffers & RendererBuffer_Stencil))
|
||||
{
|
||||
NazaraError("Filter cannot be bilinear when blitting depth/stencil buffers");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
GLbitfield mask = 0;
|
||||
if (buffers & RendererBuffer_Color)
|
||||
mask |= GL_COLOR_BUFFER_BIT;
|
||||
|
||||
if (buffers & RendererBuffer_Depth)
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
|
||||
if (buffers & RendererBuffer_Stencil)
|
||||
mask |= GL_STENCIL_BUFFER_BIT;
|
||||
|
||||
GLint previousDrawBuffer, previousReadBuffer;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previousDrawBuffer);
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &previousReadBuffer);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->GetOpenGLID());
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, src->GetOpenGLID());
|
||||
|
||||
glBlitFramebuffer(srcRect.x, srcRect.y, srcRect.x + srcRect.width, srcRect.y + srcRect.height,
|
||||
dstRect.x, dstRect.y, dstRect.x + dstRect.width, dstRect.y + dstRect.height,
|
||||
mask, (bilinearFilter) ? GL_LINEAR : GL_NEAREST);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previousDrawBuffer);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, previousReadBuffer);
|
||||
}
|
||||
|
||||
bool RenderTexture::Activate() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (Context::GetCurrent() != m_impl->context)
|
||||
{
|
||||
NazaraError("RenderTexture cannot be used with this context");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_impl->fbo);
|
||||
|
||||
m_drawBuffersUpdated = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderTexture::Desactivate() const
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid render texture");
|
||||
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
if (Context::GetCurrent() != m_impl->context)
|
||||
{
|
||||
NazaraError("RenderTexture cannot be used with this context");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
glFlush();
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void RenderTexture::EnsureTargetUpdated() const
|
||||
{
|
||||
if (!m_drawBuffersUpdated)
|
||||
UpdateDrawBuffers();
|
||||
|
||||
for (UInt8 index : m_impl->colorTargets)
|
||||
{
|
||||
Attachment& attachment = m_impl->attachments[attachmentIndex[AttachmentPoint_Color] + index];
|
||||
if (!attachment.isBuffer)
|
||||
attachment.texture->InvalidateMipmaps();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTexture::OnContextDestroy(const Context* context)
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid internal state");
|
||||
NazaraUnused(context);
|
||||
|
||||
#ifdef NAZARA_DEBUG
|
||||
if (m_impl->context != context)
|
||||
{
|
||||
NazaraInternalError("Not listening to " + String::Pointer(context));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void RenderTexture::OnRenderBufferDestroy(const RenderBuffer* renderBuffer, unsigned int attachmentIndex)
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid internal state");
|
||||
NazaraAssert(attachmentIndex < m_impl->attachments.size(), "Invalid attachment index");
|
||||
NazaraAssert(m_impl->attachments[attachmentIndex].isBuffer, "Invalid attachment state");
|
||||
NazaraUnused(renderBuffer);
|
||||
|
||||
Attachment& attachment = m_impl->attachments[attachmentIndex];
|
||||
attachment.buffer = nullptr;
|
||||
attachment.isUsed = false;
|
||||
attachment.renderBufferDestroySlot.Disconnect();
|
||||
|
||||
InvalidateTargets();
|
||||
}
|
||||
|
||||
void RenderTexture::OnTextureDestroy(const Texture* texture, unsigned int attachmentIndex)
|
||||
{
|
||||
NazaraAssert(m_impl, "Invalid internal state");
|
||||
NazaraAssert(attachmentIndex < m_impl->attachments.size(), "Invalid attachment index");
|
||||
NazaraAssert(!m_impl->attachments[attachmentIndex].isBuffer, "Invalid attachment state");
|
||||
NazaraUnused(texture);
|
||||
NazaraUnused(attachmentIndex);
|
||||
|
||||
InvalidateTargets();
|
||||
}
|
||||
|
||||
void RenderTexture::UpdateDrawBuffers() const
|
||||
{
|
||||
if (!m_targetsUpdated)
|
||||
UpdateTargets();
|
||||
|
||||
glDrawBuffers(m_impl->drawBuffers.size(), &m_impl->drawBuffers[0]);
|
||||
|
||||
m_drawBuffersUpdated = true;
|
||||
}
|
||||
|
||||
void RenderTexture::UpdateSize() const
|
||||
{
|
||||
m_impl->width = 0;
|
||||
m_impl->height = 0;
|
||||
for (Attachment& attachment : m_impl->attachments)
|
||||
{
|
||||
if (attachment.isUsed)
|
||||
{
|
||||
m_impl->height = std::max(m_impl->height, attachment.height);
|
||||
m_impl->width = std::max(m_impl->width, attachment.width);
|
||||
}
|
||||
}
|
||||
|
||||
m_sizeUpdated = true;
|
||||
}
|
||||
|
||||
void RenderTexture::UpdateTargets() const
|
||||
{
|
||||
if (!m_impl->userDefinedTargets)
|
||||
{
|
||||
m_impl->colorTargets.clear();
|
||||
|
||||
unsigned int colorIndex = 0;
|
||||
for (unsigned int index = attachmentIndex[AttachmentPoint_Color]; index < m_impl->attachments.size(); ++index)
|
||||
m_impl->colorTargets.push_back(colorIndex++);
|
||||
}
|
||||
|
||||
if (m_impl->colorTargets.empty())
|
||||
{
|
||||
m_impl->drawBuffers.resize(1);
|
||||
m_impl->drawBuffers[0] = GL_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_impl->drawBuffers.resize(m_impl->colorTargets.size());
|
||||
GLenum* ptr = &m_impl->drawBuffers[0];
|
||||
for (UInt8 index : m_impl->colorTargets)
|
||||
*ptr++ = GL_COLOR_ATTACHMENT0 + index;
|
||||
}
|
||||
|
||||
m_targetsUpdated = true;
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,6 @@
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
RenderWindow::~RenderWindow()
|
||||
{
|
||||
// Nécessaire si Window::Destroy est appelé par son destructeur
|
||||
OnWindowDestroy();
|
||||
}
|
||||
|
||||
void RenderWindow::Display()
|
||||
{
|
||||
if (m_framerateLimit > 0)
|
||||
|
||||
@@ -81,15 +81,14 @@ namespace Nz
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Création du mesh
|
||||
// Le moteur ne supporte plus les animations image-clé, nous ne pouvons charger qu'en statique
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
// Since the engine no longer supports keyframe animations, let's make a static mesh
|
||||
if (!mesh->CreateStatic())
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Chargement des skins
|
||||
// Extract skins (texture name)
|
||||
if (header.num_skins > 0)
|
||||
{
|
||||
mesh->SetMaterialCount(header.num_skins);
|
||||
@@ -109,16 +108,15 @@ namespace Nz
|
||||
}
|
||||
}
|
||||
|
||||
/// Chargement des submesh
|
||||
// Actuellement le loader ne charge qu'un submesh
|
||||
IndexBufferRef indexBuffer = IndexBuffer::New(false, header.num_tris*3, parameters.storage, BufferUsage_Static);
|
||||
|
||||
/// Lecture des triangles
|
||||
// Extract triangles data
|
||||
std::vector<MD2_Triangle> triangles(header.num_tris);
|
||||
|
||||
stream.SetCursorPos(header.offset_tris);
|
||||
stream.Read(&triangles[0], header.num_tris*sizeof(MD2_Triangle));
|
||||
|
||||
// And convert them into an index buffer
|
||||
BufferMapper<IndexBuffer> indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
|
||||
UInt16* index = static_cast<UInt16*>(indexMapper.GetPointer());
|
||||
|
||||
@@ -135,7 +133,7 @@ namespace Nz
|
||||
SwapBytes(&triangles[i].texCoords[2], sizeof(UInt16));
|
||||
#endif
|
||||
|
||||
// On respécifie le triangle dans l'ordre attendu
|
||||
// Reverse winding order
|
||||
*index++ = triangles[i].vertices[0];
|
||||
*index++ = triangles[i].vertices[2];
|
||||
*index++ = triangles[i].vertices[1];
|
||||
@@ -143,10 +141,11 @@ namespace Nz
|
||||
|
||||
indexMapper.Unmap();
|
||||
|
||||
// Optimize if requested (improves cache locality)
|
||||
if (parameters.optimizeIndexBuffers)
|
||||
indexBuffer->Optimize();
|
||||
|
||||
/// Lecture des coordonnées de texture
|
||||
// Extracting texture coordinates
|
||||
std::vector<MD2_TexCoord> texCoords(header.num_st);
|
||||
|
||||
stream.SetCursorPos(header.offset_st);
|
||||
@@ -168,15 +167,15 @@ namespace Nz
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Chargement des vertices
|
||||
// Extracting vertices
|
||||
stream.SetCursorPos(header.offset_frames);
|
||||
|
||||
std::unique_ptr<MD2_Vertex[]> vertices(new MD2_Vertex[header.num_vertices]);
|
||||
std::vector<MD2_Vertex> vertices(header.num_vertices);
|
||||
Vector3f scale, translate;
|
||||
stream.Read(scale, sizeof(Vector3f));
|
||||
stream.Read(translate, sizeof(Vector3f));
|
||||
stream.Read(nullptr, 16*sizeof(char)); // Nom de la frame, inutile ici
|
||||
stream.Read(vertices.get(), header.num_vertices*sizeof(MD2_Vertex));
|
||||
stream.Read(nullptr, 16*sizeof(char)); //< Frame name, unused
|
||||
stream.Read(vertices.data(), header.num_vertices*sizeof(MD2_Vertex));
|
||||
|
||||
#ifdef NAZARA_BIG_ENDIAN
|
||||
SwapBytes(&scale.x, sizeof(float));
|
||||
@@ -196,23 +195,26 @@ namespace Nz
|
||||
BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_DiscardAndWrite);
|
||||
MeshVertex* vertex = static_cast<MeshVertex*>(vertexMapper.GetPointer());
|
||||
|
||||
/// Chargement des coordonnées de texture
|
||||
const unsigned int indexFix[3] = {0, 2, 1}; // Pour respécifier les indices dans le bon ordre
|
||||
// Loading texture coordinates
|
||||
const unsigned int indexFix[3] = {0, 2, 1};
|
||||
Vector2f invSkinSize(1.f / header.skinwidth, 1.f / header.skinheight);
|
||||
for (unsigned int i = 0; i < header.num_tris; ++i)
|
||||
{
|
||||
for (unsigned int j = 0; j < 3; ++j)
|
||||
{
|
||||
const unsigned int fixedIndex = indexFix[j];
|
||||
const MD2_TexCoord& texC = texCoords[triangles[i].texCoords[fixedIndex]];
|
||||
float u = static_cast<float>(texC.u) / header.skinwidth;
|
||||
float v = static_cast<float>(texC.v) / header.skinheight;
|
||||
const unsigned int fixedIndex = indexFix[j]; //< Reverse winding order
|
||||
|
||||
vertex[triangles[i].vertices[fixedIndex]].uv.Set(u, (parameters.flipUVs) ? 1.f - v : v);
|
||||
const MD2_TexCoord& texC = texCoords[triangles[i].texCoords[fixedIndex]];
|
||||
Vector2f uv(texC.u, texC.v);
|
||||
uv *= invSkinSize;
|
||||
|
||||
vertex[triangles[i].vertices[fixedIndex]].uv.Set(parameters.texCoordOffset + uv * parameters.texCoordScale);
|
||||
}
|
||||
}
|
||||
|
||||
/// Chargement des positions
|
||||
// Pour que le modèle soit correctement aligné, on génère un quaternion que nous appliquerons à chacune des vertices
|
||||
// Loading vertex position
|
||||
|
||||
// Align the model to our coordinates system
|
||||
Quaternionf rotationQuat = EulerAnglesf(-90.f, 90.f, 0.f);
|
||||
Nz::Matrix4f matrix = Matrix4f::Transform(translate, rotationQuat, scale);
|
||||
matrix *= parameters.matrix;
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace Nz
|
||||
}
|
||||
|
||||
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->uv.Set(parameters.texCoordOffset + vertex.uv * parameters.texCoordScale);
|
||||
vertices++;
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace Nz
|
||||
VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage);
|
||||
BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);
|
||||
|
||||
MeshVertex* vertex = static_cast<MeshVertex*>(vertexMapper.GetPointer());
|
||||
MeshVertex* vertices = static_cast<MeshVertex*>(vertexMapper.GetPointer());
|
||||
for (const MD5MeshParser::Vertex& md5Vertex : md5Mesh.vertices)
|
||||
{
|
||||
// Skinning MD5 (Formule d'Id Tech)
|
||||
@@ -268,9 +268,9 @@ namespace Nz
|
||||
}
|
||||
|
||||
// On retourne le modèle dans le bon sens
|
||||
vertex->position = matrix * finalPos;
|
||||
vertex->uv.Set(md5Vertex.uv.x, (parameters.flipUVs) ? 1.f - md5Vertex.uv.y : md5Vertex.uv.y); // Inversion des UV si demandé
|
||||
vertex++;
|
||||
vertices->position = matrix * finalPos;
|
||||
vertices->uv.Set(parameters.texCoordOffset + md5Vertex.uv * parameters.texCoordScale);
|
||||
vertices++;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
@@ -265,8 +265,8 @@ namespace Nz
|
||||
|
||||
if (vertexIndices.texCoord > 0)
|
||||
{
|
||||
const Vector3f& uvw = texCoords[vertexIndices.texCoord-1];
|
||||
vertex.uv.Set(uvw.x, (parameters.flipUVs) ? 1.f - uvw.y : uvw.y); // Inversion des UVs si demandé
|
||||
Vector2f uv = Vector2f(texCoords[vertexIndices.texCoord - 1]);
|
||||
vertex.uv.Set(parameters.texCoordOffset + uv * parameters.texCoordScale);
|
||||
}
|
||||
else
|
||||
hasTexCoords = false;
|
||||
|
||||
@@ -103,6 +103,24 @@ namespace Nz
|
||||
return m_glyphs.size();
|
||||
}
|
||||
|
||||
const AbstractTextDrawer::Line& SimpleTextDrawer::GetLine(std::size_t index) const
|
||||
{
|
||||
NazaraAssert(index < m_lines.size(), "Line index out of range");
|
||||
|
||||
if (!m_glyphUpdated)
|
||||
UpdateGlyphs();
|
||||
|
||||
return m_lines[index];
|
||||
}
|
||||
|
||||
std::size_t SimpleTextDrawer::GetLineCount() const
|
||||
{
|
||||
if (!m_glyphUpdated)
|
||||
UpdateGlyphs();
|
||||
|
||||
return m_lines.size();
|
||||
}
|
||||
|
||||
UInt32 SimpleTextDrawer::GetStyle() const
|
||||
{
|
||||
return m_style;
|
||||
@@ -218,10 +236,16 @@ namespace Nz
|
||||
m_bounds.MakeZero();
|
||||
m_colorUpdated = true;
|
||||
m_drawPos.Set(0, m_characterSize); //< Our draw "cursor"
|
||||
m_lines.clear();
|
||||
m_glyphs.clear();
|
||||
m_glyphUpdated = true;
|
||||
m_previousCharacter = 0;
|
||||
m_workingBounds.MakeZero(); //< Compute bounds as float to speedup bounds computation (as casting between floats and integers is costly)
|
||||
|
||||
if (m_font)
|
||||
m_lines.emplace_back(Line{Rectf(0.f, 0.f, 0.f, m_font->GetSizeInfo(m_characterSize).lineHeight), 0});
|
||||
else
|
||||
m_lines.emplace_back(Line{Rectf::Zero(), 0});
|
||||
}
|
||||
|
||||
void SimpleTextDrawer::ConnectFontSlots()
|
||||
@@ -264,19 +288,16 @@ namespace Nz
|
||||
m_previousCharacter = character;
|
||||
|
||||
bool whitespace = true;
|
||||
int advance = 0;
|
||||
switch (character)
|
||||
{
|
||||
case ' ':
|
||||
m_drawPos.x += sizeInfo.spaceAdvance;
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
m_drawPos.x = 0;
|
||||
m_drawPos.y += sizeInfo.lineHeight;
|
||||
advance = sizeInfo.spaceAdvance;
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
m_drawPos.x += sizeInfo.spaceAdvance * 4;
|
||||
advance = sizeInfo.spaceAdvance * 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -284,61 +305,93 @@ namespace Nz
|
||||
break;
|
||||
}
|
||||
|
||||
if (whitespace)
|
||||
continue; // White spaces are blanks and invisible, move the draw position and skip the rest
|
||||
|
||||
const Font::Glyph& fontGlyph = m_font->GetGlyph(m_characterSize, m_style, character);
|
||||
if (!fontGlyph.valid)
|
||||
continue; // Glyph failed to load, just skip it (can't do much)
|
||||
|
||||
Glyph glyph;
|
||||
glyph.atlas = m_font->GetAtlas()->GetLayer(fontGlyph.layerIndex);
|
||||
glyph.atlasRect = fontGlyph.atlasRect;
|
||||
glyph.color = m_color;
|
||||
glyph.flipped = fontGlyph.flipped;
|
||||
|
||||
int advance = fontGlyph.advance;
|
||||
|
||||
Rectf bounds(fontGlyph.aabb);
|
||||
bounds.x += m_drawPos.x;
|
||||
bounds.y += m_drawPos.y;
|
||||
|
||||
if (fontGlyph.requireFauxBold)
|
||||
if (!whitespace)
|
||||
{
|
||||
// Let's simulate bold by enlarging the glyph (not a neat idea, but should work)
|
||||
Vector2f center = bounds.GetCenter();
|
||||
const Font::Glyph& fontGlyph = m_font->GetGlyph(m_characterSize, m_style, character);
|
||||
if (!fontGlyph.valid)
|
||||
continue; // Glyph failed to load, just skip it (can't do much)
|
||||
|
||||
// Enlarge by 10%
|
||||
bounds.width *= 1.1f;
|
||||
bounds.height *= 1.1f;
|
||||
advance = fontGlyph.advance;
|
||||
|
||||
// Replace it at the correct height
|
||||
Vector2f offset(bounds.GetCenter() - center);
|
||||
bounds.y -= offset.y;
|
||||
glyph.atlas = m_font->GetAtlas()->GetLayer(fontGlyph.layerIndex);
|
||||
glyph.atlasRect = fontGlyph.atlasRect;
|
||||
glyph.color = m_color;
|
||||
glyph.flipped = fontGlyph.flipped;
|
||||
|
||||
// Adjust advance (+10%)
|
||||
advance += advance / 10;
|
||||
if (fontGlyph.requireFauxBold)
|
||||
{
|
||||
// Let's simulate bold by enlarging the glyph (not a neat idea, but should work)
|
||||
Vector2f center = glyph.bounds.GetCenter();
|
||||
|
||||
// Enlarge by 10%
|
||||
glyph.bounds.width *= 1.1f;
|
||||
glyph.bounds.height *= 1.1f;
|
||||
|
||||
// Replace it at the correct height
|
||||
Vector2f offset(glyph.bounds.GetCenter() - center);
|
||||
glyph.bounds.y -= offset.y;
|
||||
|
||||
// Adjust advance (+10%)
|
||||
advance += advance / 10;
|
||||
}
|
||||
|
||||
glyph.bounds.Set(fontGlyph.aabb);
|
||||
glyph.bounds.x += m_drawPos.x;
|
||||
glyph.bounds.y += m_drawPos.y;
|
||||
|
||||
// We "lean" the glyph to simulate italics style
|
||||
float italic = (fontGlyph.requireFauxItalic) ? 0.208f : 0.f;
|
||||
float italicTop = italic * glyph.bounds.y;
|
||||
float italicBottom = italic * glyph.bounds.GetMaximum().y;
|
||||
|
||||
glyph.corners[0].Set(glyph.bounds.x - italicTop, glyph.bounds.y);
|
||||
glyph.corners[1].Set(glyph.bounds.x + glyph.bounds.width - italicTop, glyph.bounds.y);
|
||||
glyph.corners[2].Set(glyph.bounds.x - italicBottom, glyph.bounds.y + glyph.bounds.height);
|
||||
glyph.corners[3].Set(glyph.bounds.x + glyph.bounds.width - italicBottom, glyph.bounds.y + glyph.bounds.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
glyph.atlas = nullptr;
|
||||
|
||||
glyph.bounds.Set(m_drawPos.x, m_drawPos.y, float(advance), sizeInfo.lineHeight);
|
||||
|
||||
glyph.corners[0].Set(glyph.bounds.GetCorner(RectCorner_LeftTop));
|
||||
glyph.corners[1].Set(glyph.bounds.GetCorner(RectCorner_RightTop));
|
||||
glyph.corners[2].Set(glyph.bounds.GetCorner(RectCorner_LeftBottom));
|
||||
glyph.corners[3].Set(glyph.bounds.GetCorner(RectCorner_RightBottom));
|
||||
|
||||
switch (character)
|
||||
{
|
||||
case '\n':
|
||||
{
|
||||
if (!m_glyphs.empty())
|
||||
{
|
||||
Glyph& glyph = m_glyphs.back();
|
||||
m_lines.back().bounds.ExtendTo(glyph.bounds);
|
||||
}
|
||||
|
||||
advance = 0;
|
||||
m_drawPos.x = 0;
|
||||
m_drawPos.y += sizeInfo.lineHeight;
|
||||
|
||||
m_lines.emplace_back(Line{Rectf(0.f, sizeInfo.lineHeight * m_lines.size(), 0.f, sizeInfo.lineHeight), m_glyphs.size() + 1});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We "lean" the glyph to simulate italics style
|
||||
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);
|
||||
m_lines.back().bounds.ExtendTo(glyph.bounds);
|
||||
|
||||
if (!m_workingBounds.IsValid())
|
||||
m_workingBounds.Set(glyph.corners[0]);
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
m_workingBounds.ExtendTo(glyph.corners[i]);
|
||||
m_workingBounds.Set(glyph.bounds);
|
||||
else
|
||||
m_workingBounds.ExtendTo(glyph.bounds);
|
||||
|
||||
m_drawPos.x += advance;
|
||||
m_glyphs.push_back(glyph);
|
||||
}
|
||||
m_lines.back().bounds.ExtendTo(m_glyphs.back().bounds);
|
||||
|
||||
m_bounds.Set(Rectf(std::floor(m_workingBounds.x), std::floor(m_workingBounds.y), std::ceil(m_workingBounds.width), std::ceil(m_workingBounds.height)));
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
bool WindowImpl::Create(const VideoMode& mode, const String& title, UInt32 style)
|
||||
bool WindowImpl::Create(const VideoMode& mode, const String& title, WindowStyleFlags style)
|
||||
{
|
||||
bool async = (style & WindowStyle_Threaded) != 0;
|
||||
bool fullscreen = (style & WindowStyle_Fullscreen) != 0;
|
||||
@@ -259,7 +259,7 @@ namespace Nz
|
||||
return m_size;
|
||||
}
|
||||
|
||||
UInt32 WindowImpl::GetStyle() const
|
||||
WindowStyleFlags WindowImpl::GetStyle() const
|
||||
{
|
||||
return m_style;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Nz
|
||||
WindowImpl(WindowImpl&&) = delete; ///TODO?
|
||||
~WindowImpl() = default;
|
||||
|
||||
bool Create(const VideoMode& mode, const String& title, UInt32 style);
|
||||
bool Create(const VideoMode& mode, const String& title, WindowStyleFlags style);
|
||||
bool Create(WindowHandle handle);
|
||||
|
||||
void Destroy();
|
||||
@@ -49,7 +49,7 @@ namespace Nz
|
||||
unsigned int GetHeight() const;
|
||||
Vector2i GetPosition() const;
|
||||
Vector2ui GetSize() const;
|
||||
UInt32 GetStyle() const;
|
||||
WindowStyleFlags GetStyle() const;
|
||||
String GetTitle() const;
|
||||
unsigned int GetWidth() const;
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Nz
|
||||
HCURSOR m_cursor;
|
||||
HWND m_handle;
|
||||
LONG_PTR m_callback;
|
||||
UInt32 m_style;
|
||||
WindowStyleFlags m_style;
|
||||
Vector2i m_maxSize;
|
||||
Vector2i m_minSize;
|
||||
Vector2i m_mousePos;
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Nz
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool Window::Create(VideoMode mode, const String& title, UInt32 style)
|
||||
bool Window::Create(VideoMode mode, const String& title, WindowStyleFlags style)
|
||||
{
|
||||
// Si la fenêtre est déjà ouverte, nous conservons sa position
|
||||
bool opened = IsOpen();
|
||||
@@ -228,7 +228,7 @@ namespace Nz
|
||||
return m_impl->GetSize();
|
||||
}
|
||||
|
||||
UInt32 Window::GetStyle() const
|
||||
WindowStyleFlags Window::GetStyle() const
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
if (!m_impl)
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Nz
|
||||
UpdateEventQueue(nullptr);
|
||||
}
|
||||
|
||||
bool WindowImpl::Create(const VideoMode& mode, const String& title, UInt32 style)
|
||||
bool WindowImpl::Create(const VideoMode& mode, const String& title, WindowStyleFlags style)
|
||||
{
|
||||
bool fullscreen = (style & Nz::WindowStyle_Fullscreen) != 0;
|
||||
m_eventListener = true;
|
||||
@@ -336,7 +336,7 @@ namespace Nz
|
||||
return Vector2ui(m_size_hints.width, m_size_hints.height);
|
||||
}
|
||||
|
||||
UInt32 WindowImpl::GetStyle() const
|
||||
WindowStyleFlags WindowImpl::GetStyle() const
|
||||
{
|
||||
return m_style;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Nz
|
||||
WindowImpl(WindowImpl&&) = delete; ///TODO?
|
||||
~WindowImpl();
|
||||
|
||||
bool Create(const VideoMode& mode, const String& title, UInt32 style);
|
||||
bool Create(const VideoMode& mode, const String& title, WindowStyleFlags style);
|
||||
bool Create(WindowHandle handle);
|
||||
|
||||
void Destroy();
|
||||
@@ -47,7 +47,7 @@ namespace Nz
|
||||
unsigned int GetHeight() const;
|
||||
Vector2i GetPosition() const;
|
||||
Vector2ui GetSize() const;
|
||||
UInt32 GetStyle() const;
|
||||
WindowStyleFlags GetStyle() const;
|
||||
String GetTitle() const;
|
||||
unsigned int GetWidth() const;
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace Nz
|
||||
xcb_randr_get_screen_info_reply_t m_oldVideoMode;
|
||||
xcb_size_hints_t m_size_hints;
|
||||
Thread m_thread;
|
||||
UInt32 m_style;
|
||||
WindowStyleFlags m_style;
|
||||
Window* m_parent;
|
||||
bool m_eventListener;
|
||||
bool m_ownsWindow;
|
||||
|
||||
Reference in New Issue
Block a user