diff --git a/include/Nazara/Renderer/Texture.hpp b/include/Nazara/Renderer/Texture.hpp index 250727917..55f2972f2 100644 --- a/include/Nazara/Renderer/Texture.hpp +++ b/include/Nazara/Renderer/Texture.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -54,11 +55,23 @@ class NAZARA_API NzTexture : public NzResource, NzNonCopyable bool IsTarget() const; bool IsValid() const; + // Load bool LoadFromFile(const NzString& filePath, const NzImageParams& params = NzImageParams(), bool generateMipmaps = true); bool LoadFromImage(const NzImage& image, bool generateMipmaps = true); bool LoadFromMemory(const void* data, std::size_t size, const NzImageParams& params = NzImageParams(), bool generateMipmaps = true); bool LoadFromStream(NzInputStream& stream, const NzImageParams& params = NzImageParams(), bool generateMipmaps = true); + // LoadCubemap + bool LoadCubemapFromFile(const NzString& filePath, const NzImageParams& imageParams = NzImageParams(), bool generateMipmaps = true, const NzCubemapParams& cubemapParams = NzCubemapParams()); + bool LoadCubemapFromImage(const NzImage& image, bool generateMipmaps = true, const NzCubemapParams& params = NzCubemapParams()); + bool LoadCubemapFromMemory(const void* data, std::size_t size, const NzImageParams& imageParams = NzImageParams(), bool generateMipmaps = true, const NzCubemapParams& cubemapParams = NzCubemapParams()); + bool LoadCubemapFromStream(NzInputStream& stream, const NzImageParams& imageParams = NzImageParams(), bool generateMipmaps = true, const NzCubemapParams& cubemapParams = NzCubemapParams()); + + // LoadFace + bool LoadFaceFromFile(nzCubemapFace face, const NzString& filePath, const NzImageParams& params = NzImageParams()); + bool LoadFaceFromMemory(nzCubemapFace face, const void* data, std::size_t size, const NzImageParams& params = NzImageParams()); + bool LoadFaceFromStream(nzCubemapFace face, NzInputStream& stream, const NzImageParams& params = NzImageParams()); + bool Lock(); bool SetMipmapRange(nzUInt8 minLevel, nzUInt8 maxLevel); diff --git a/include/Nazara/Utility/CubemapParams.hpp b/include/Nazara/Utility/CubemapParams.hpp new file mode 100644 index 000000000..d291caf9e --- /dev/null +++ b/include/Nazara/Utility/CubemapParams.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2013 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_CUBEMAPPARAMS_HPP +#define NAZARA_CUBEMAPPARAMS_HPP + +#include + +struct NzCubemapParams +{ + /* + La position de chaque face dans la cubemap + Les indices ici seront multipliés à la taille d'une face pour obtenir le coin haut-gauche de la zone. + Si la taille d'une face est 0, elle sera calculée via max(width, height)/4. + + Par défaut, cela suit ce layout : + U + L F R B + D + + Si ce n'est pas le cas, à vous de repositionner les faces correctement. + */ + NzVector2ui backPosition = NzVector2ui(3, 1); + NzVector2ui downPosition = NzVector2ui(1, 2); + NzVector2ui forwardPosition = NzVector2ui(1, 1); + NzVector2ui leftPosition = NzVector2ui(0, 1); + NzVector2ui rightPosition = NzVector2ui(2, 1); + NzVector2ui upPosition = NzVector2ui(1, 0); + unsigned int faceSize = 0; +}; + +#endif // NAZARA_CUBEMAPPARAMS_HPP diff --git a/include/Nazara/Utility/Image.hpp b/include/Nazara/Utility/Image.hpp index 7d6a2d42e..dc9dec4d0 100644 --- a/include/Nazara/Utility/Image.hpp +++ b/include/Nazara/Utility/Image.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -90,10 +91,17 @@ class NAZARA_API NzImage : public NzResource bool IsCubemap() const; bool IsValid() const; + // Load bool LoadFromFile(const NzString& filePath, const NzImageParams& params = NzImageParams()); bool LoadFromMemory(const void* data, std::size_t size, const NzImageParams& params = NzImageParams()); bool LoadFromStream(NzInputStream& stream, const NzImageParams& params = NzImageParams()); + // LoadCubemap + bool LoadCubemapFromFile(const NzString& filePath, const NzImageParams& imageParams = NzImageParams(), const NzCubemapParams& cubemapParams = NzCubemapParams()); + bool LoadCubemapFromImage(const NzImage& image, const NzCubemapParams& params = NzCubemapParams()); + bool LoadCubemapFromMemory(const void* data, std::size_t size, const NzImageParams& imageParams = NzImageParams(), const NzCubemapParams& cubemapParams = NzCubemapParams()); + bool LoadCubemapFromStream(NzInputStream& stream, const NzImageParams& imageParams = NzImageParams(), const NzCubemapParams& cubemapParams = NzCubemapParams()); + void SetLevelCount(nzUInt8 levelCount); bool SetPixelColor(const NzColor& color, unsigned int x, unsigned int y = 0, unsigned int z = 0); diff --git a/src/Nazara/Renderer/Texture.cpp b/src/Nazara/Renderer/Texture.cpp index 446eb139a..1e32a5178 100644 --- a/src/Nazara/Renderer/Texture.cpp +++ b/src/Nazara/Renderer/Texture.cpp @@ -704,6 +704,248 @@ bool NzTexture::LoadFromStream(NzInputStream& stream, const NzImageParams& param return LoadFromImage(image, generateMipmaps); } +bool NzTexture::LoadCubemapFromFile(const NzString& filePath, const NzImageParams& imageParams, bool generateMipmaps, const NzCubemapParams& cubemapParams) +{ + NzImage image; + if (!image.LoadFromFile(filePath, imageParams)) + { + NazaraError("Failed to load image"); + return false; + } + + return LoadCubemapFromImage(image, generateMipmaps, cubemapParams); +} + +bool NzTexture::LoadCubemapFromImage(const NzImage& image, bool generateMipmaps, const NzCubemapParams& params) +{ + // Implémentation presque identique à celle de Image::LoadCubemapFromImage + #if NAZARA_UTILITY_SAFE + if (!image.IsValid()) + { + NazaraError("Image must be valid"); + return false; + } + #endif + + unsigned int width = image.GetWidth(); + unsigned int height = image.GetHeight(); + unsigned int faceSize = (params.faceSize == 0) ? std::max(width, height)/4 : params.faceSize; + + // Sans cette vérification, celles des rectangles pourrait réussir via un overflow + if (width < faceSize || height < faceSize) + { + NazaraError("Image is too small for this face size"); + return false; + } + + // Calcul et vérification des surfaces + unsigned limitX = width - faceSize; + unsigned limitY = height - faceSize; + + NzVector2ui backPos = params.backPosition * faceSize; + if (backPos.x > limitX || backPos.y > limitY) + { + NazaraError("Back rectangle is out of image"); + return false; + } + + NzVector2ui downPos = params.downPosition * faceSize; + if (downPos.x > limitX || downPos.y > limitY) + { + NazaraError("Down rectangle is out of image"); + return false; + } + + NzVector2ui forwardPos = params.forwardPosition * faceSize; + if (forwardPos.x > limitX || forwardPos.y > limitY) + { + NazaraError("Forward rectangle is out of image"); + return false; + } + + NzVector2ui leftPos = params.leftPosition * faceSize; + if (leftPos.x > limitX || leftPos.y > limitY) + { + NazaraError("Left rectangle is out of image"); + return false; + } + + NzVector2ui rightPos = params.rightPosition * faceSize; + if (rightPos.x > limitX || rightPos.y > limitY) + { + NazaraError("Right rectangle is out of image"); + return false; + } + + NzVector2ui upPos = params.upPosition * faceSize; + if (upPos.x > limitX || upPos.y > limitY) + { + NazaraError("Up rectangle is out of image"); + return false; + } + + if (!Create(nzImageType_Cubemap, image.GetFormat(), faceSize, faceSize, 1, (generateMipmaps) ? 0xFF : 1, true)) + { + NazaraError("Failed to create texture"); + return false; + } + + UpdateFace(nzCubemapFace_NegativeX, image.GetConstPixels(leftPos.x, leftPos.y), width, height); + UpdateFace(nzCubemapFace_NegativeY, image.GetConstPixels(downPos.x, downPos.y), width, height); + UpdateFace(nzCubemapFace_NegativeZ, image.GetConstPixels(backPos.x, backPos.y), width, height); + UpdateFace(nzCubemapFace_PositiveX, image.GetConstPixels(rightPos.x, rightPos.y), width, height); + UpdateFace(nzCubemapFace_PositiveY, image.GetConstPixels(upPos.x, upPos.y), width, height); + UpdateFace(nzCubemapFace_PositiveZ, image.GetConstPixels(forwardPos.x, forwardPos.y), width, height); + + UnlockTexture(m_impl); + + return true; +} + +bool NzTexture::LoadCubemapFromMemory(const void* data, std::size_t size, const NzImageParams& imageParams, bool generateMipmaps, const NzCubemapParams& cubemapParams) +{ + NzImage image; + if (!image.LoadFromMemory(data, size, imageParams)) + { + NazaraError("Failed to load image"); + return false; + } + + return LoadCubemapFromImage(image, generateMipmaps, cubemapParams); +} + +bool NzTexture::LoadCubemapFromStream(NzInputStream& stream, const NzImageParams& imageParams, bool generateMipmaps, const NzCubemapParams& cubemapParams) +{ + NzImage image; + if (!image.LoadFromStream(stream, imageParams)) + { + NazaraError("Failed to load image"); + return false; + } + + return LoadCubemapFromImage(image, generateMipmaps, cubemapParams); +} + +bool NzTexture::LoadFaceFromFile(nzCubemapFace face, const NzString& filePath, const NzImageParams& params) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + + if (m_impl->type != nzImageType_Cubemap) + { + NazaraError("Texture must be a cubemap"); + return false; + } + #endif + + NzImage image; + if (!image.LoadFromFile(filePath, params)) + { + NazaraError("Failed to load image"); + return false; + } + + if (!image.Convert(m_impl->format)) + { + NazaraError("Failed to convert image to texture format"); + return false; + } + + unsigned int faceSize = m_impl->width; + + if (image.GetWidth() != faceSize || image.GetHeight() != faceSize) + { + NazaraError("Image size must match texture face size"); + return false; + } + + return UpdateFace(face, image); +} + +bool NzTexture::LoadFaceFromMemory(nzCubemapFace face, const void* data, std::size_t size, const NzImageParams& params) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + + if (m_impl->type != nzImageType_Cubemap) + { + NazaraError("Texture must be a cubemap"); + return false; + } + #endif + + NzImage image; + if (!image.LoadFromMemory(data, size, params)) + { + NazaraError("Failed to load image"); + return false; + } + + if (!image.Convert(m_impl->format)) + { + NazaraError("Failed to convert image to texture format"); + return false; + } + + unsigned int faceSize = m_impl->width; + + if (image.GetWidth() != faceSize || image.GetHeight() != faceSize) + { + NazaraError("Image size must match texture face size"); + return false; + } + + return UpdateFace(face, image); +} + +bool NzTexture::LoadFaceFromStream(nzCubemapFace face, NzInputStream& stream, const NzImageParams& params) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + + if (m_impl->type != nzImageType_Cubemap) + { + NazaraError("Texture must be a cubemap"); + return false; + } + #endif + + NzImage image; + if (!image.LoadFromStream(stream, params)) + { + NazaraError("Failed to load image"); + return false; + } + + if (!image.Convert(m_impl->format)) + { + NazaraError("Failed to convert image to texture format"); + return false; + } + + unsigned int faceSize = m_impl->width; + + if (image.GetWidth() != faceSize || image.GetHeight() != faceSize) + { + NazaraError("Image size must match texture face size"); + return false; + } + + return UpdateFace(face, image); +} + bool NzTexture::Lock() { #if NAZARA_RENDERER_SAFE diff --git a/src/Nazara/Utility/Image.cpp b/src/Nazara/Utility/Image.cpp index d71838dfb..f5418bc9b 100644 --- a/src/Nazara/Utility/Image.cpp +++ b/src/Nazara/Utility/Image.cpp @@ -176,7 +176,7 @@ void NzImage::Copy(const NzImage& source, const NzCubeui& srcCube, const NzVecto } #endif - const nzUInt8* srcPtr = source.GetConstPixels(0, srcCube.x, srcCube.y, srcCube.z); + const nzUInt8* srcPtr = source.GetConstPixels(srcCube.x, srcCube.y, srcCube.z); #if NAZARA_UTILITY_SAFE if (!srcPtr) { @@ -861,6 +861,130 @@ bool NzImage::LoadFromStream(NzInputStream& stream, const NzImageParams& params) return NzImageLoader::LoadFromStream(this, stream, params); } +bool NzImage::LoadCubemapFromFile(const NzString& filePath, const NzImageParams& imageParams, const NzCubemapParams& cubemapParams) +{ + NzImage image; + if (!image.LoadFromFile(filePath, imageParams)) + { + NazaraError("Failed to load image"); + return false; + } + + return LoadCubemapFromImage(image, cubemapParams); +} + +bool NzImage::LoadCubemapFromImage(const NzImage& image, const NzCubemapParams& params) +{ + #if NAZARA_UTILITY_SAFE + if (!image.IsValid()) + { + NazaraError("Image must be valid"); + return false; + } + #endif + + unsigned int width = image.GetWidth(); + unsigned int height = image.GetHeight(); + unsigned int faceSize = (params.faceSize == 0) ? std::max(width, height)/4 : params.faceSize; + + // Sans cette vérification, celles des rectangles pourrait réussir via un overflow + if (width < faceSize || height < faceSize) + { + NazaraError("Image is too small for this face size"); + return false; + } + + // Calcul et vérification des surfaces + unsigned limitX = width - faceSize; + unsigned limitY = height - faceSize; + + NzVector2ui backPos = params.backPosition * faceSize; + if (backPos.x > limitX || backPos.y > limitY) + { + NazaraError("Back rectangle is out of image"); + return false; + } + + NzVector2ui downPos = params.downPosition * faceSize; + if (downPos.x > limitX || downPos.y > limitY) + { + NazaraError("Down rectangle is out of image"); + return false; + } + + NzVector2ui forwardPos = params.forwardPosition * faceSize; + if (forwardPos.x > limitX || forwardPos.y > limitY) + { + NazaraError("Forward rectangle is out of image"); + return false; + } + + NzVector2ui leftPos = params.leftPosition * faceSize; + if (leftPos.x > limitX || leftPos.y > limitY) + { + NazaraError("Left rectangle is out of image"); + return false; + } + + NzVector2ui rightPos = params.rightPosition * faceSize; + if (rightPos.x > limitX || rightPos.y > limitY) + { + NazaraError("Right rectangle is out of image"); + return false; + } + + NzVector2ui upPos = params.upPosition * faceSize; + if (upPos.x > limitX || upPos.y > limitY) + { + NazaraError("Up rectangle is out of image"); + return false; + } + + Create(nzImageType_Cubemap, image.GetFormat(), faceSize, faceSize); + + #ifdef NAZARA_DEBUG + // Les paramètres sont valides, que Create ne fonctionne pas relèverait d'un bug + if (m_sharedImage == &emptyImage) + { + NazaraInternalError("Failed to create cubemap"); + return false; + } + #endif + + Copy(image, NzRectui(backPos.x, backPos.y, faceSize, faceSize), NzVector3ui(0, 0, nzCubemapFace_NegativeZ)); + Copy(image, NzRectui(downPos.x, downPos.y, faceSize, faceSize), NzVector3ui(0, 0, nzCubemapFace_NegativeY)); + Copy(image, NzRectui(forwardPos.x, forwardPos.y, faceSize, faceSize), NzVector3ui(0, 0, nzCubemapFace_PositiveZ)); + Copy(image, NzRectui(leftPos.x, leftPos.y, faceSize, faceSize), NzVector3ui(0, 0, nzCubemapFace_NegativeX)); + Copy(image, NzRectui(rightPos.x, rightPos.y, faceSize, faceSize), NzVector3ui(0, 0, nzCubemapFace_PositiveX)); + Copy(image, NzRectui(upPos.x, upPos.y, faceSize, faceSize), NzVector3ui(0, 0, nzCubemapFace_PositiveY)); + + return true; +} + +bool NzImage::LoadCubemapFromMemory(const void* data, std::size_t size, const NzImageParams& imageParams, const NzCubemapParams& cubemapParams) +{ + NzImage image; + if (!image.LoadFromMemory(data, size, imageParams)) + { + NazaraError("Failed to load image"); + return false; + } + + return LoadCubemapFromImage(image, cubemapParams); +} + +bool NzImage::LoadCubemapFromStream(NzInputStream& stream, const NzImageParams& imageParams, const NzCubemapParams& cubemapParams) +{ + NzImage image; + if (!image.LoadFromStream(stream, imageParams)) + { + NazaraError("Failed to load image"); + return false; + } + + return LoadCubemapFromImage(image, cubemapParams); +} + void NzImage::SetLevelCount(nzUInt8 levelCount) { #if NAZARA_UTILITY_SAFE