Added sprite overlay rendering
Former-commit-id: 1e25a7d85f06f4a4bc3ed0ead76df01db440671d
This commit is contained in:
parent
6c4f8e7396
commit
9293022e71
|
|
@ -18,6 +18,7 @@ class NzDrawable;
|
|||
class NzLight;
|
||||
class NzMaterial;
|
||||
class NzSprite;
|
||||
class NzTexture;
|
||||
struct NzMeshData;
|
||||
|
||||
class NAZARA_API NzAbstractRenderQueue : NzNonCopyable
|
||||
|
|
@ -29,7 +30,7 @@ class NAZARA_API NzAbstractRenderQueue : NzNonCopyable
|
|||
virtual void AddDrawable(const NzDrawable* drawable) = 0;
|
||||
virtual void AddLight(const NzLight* light) = 0;
|
||||
virtual void AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) = 0;
|
||||
virtual void AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount) = 0;
|
||||
virtual void AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const NzTexture* overlay = nullptr) = 0;
|
||||
|
||||
virtual void Clear(bool fully) = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class NAZARA_API NzDeferredRenderQueue : public NzAbstractRenderQueue, NzResourc
|
|||
void AddDrawable(const NzDrawable* drawable) override;
|
||||
void AddLight(const NzLight* light) override;
|
||||
void AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) override;
|
||||
void AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount) override;
|
||||
void AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const NzTexture* overlay = nullptr) override;
|
||||
|
||||
void Clear(bool fully);
|
||||
|
||||
|
|
|
|||
|
|
@ -85,9 +85,10 @@ enum nzShaderFlags
|
|||
{
|
||||
nzShaderFlags_None = 0,
|
||||
|
||||
nzShaderFlags_Deferred = 0x1,
|
||||
nzShaderFlags_Instancing = 0x2,
|
||||
nzShaderFlags_VertexColor = 0x4,
|
||||
nzShaderFlags_Deferred = 0x1,
|
||||
nzShaderFlags_Instancing = 0x2,
|
||||
nzShaderFlags_TextureOverlay = 0x4,
|
||||
nzShaderFlags_VertexColor = 0x8,
|
||||
|
||||
nzShaderFlags_Max = nzShaderFlags_VertexColor*2-1
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource
|
|||
void AddDrawable(const NzDrawable* drawable) override;
|
||||
void AddLight(const NzLight* light) override;
|
||||
void AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) override;
|
||||
void AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount) override;
|
||||
void AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const NzTexture* overlay = nullptr) override;
|
||||
|
||||
void Clear(bool fully);
|
||||
|
||||
|
|
@ -74,7 +74,8 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource
|
|||
|
||||
typedef std::map<NzMeshData, std::pair<NzSpheref, std::vector<NzMatrix4f>>, MeshDataComparator> MeshInstanceContainer;
|
||||
typedef std::map<const NzMaterial*, std::tuple<bool, bool, MeshInstanceContainer>, BatchedModelMaterialComparator> ModelBatches;
|
||||
typedef std::map<const NzMaterial*, std::vector<SpriteChain_XYZ_Color_UV>> BasicSpriteBatches;
|
||||
typedef std::map<const NzTexture*, std::vector<SpriteChain_XYZ_Color_UV>> BasicSpriteOverlayContainer;
|
||||
typedef std::map<const NzMaterial*, BasicSpriteOverlayContainer> BasicSpriteBatches;
|
||||
typedef std::vector<const NzLight*> LightContainer;
|
||||
typedef std::vector<unsigned int> TransparentModelContainer;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,23 +32,27 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique, Nz
|
|||
void SetMaxLightPassPerObject(unsigned int passCount);
|
||||
|
||||
private:
|
||||
struct LightUniforms;
|
||||
struct ShaderUniforms;
|
||||
|
||||
void DrawBasicSprites(const NzScene* scene) const;
|
||||
void DrawOpaqueModels(const NzScene* scene) const;
|
||||
void DrawTransparentModels(const NzScene* scene) const;
|
||||
const LightUniforms* GetLightUniforms(const NzShader* shader) const;
|
||||
const ShaderUniforms* GetShaderUniforms(const NzShader* shader) const;
|
||||
|
||||
struct LightUniforms
|
||||
struct ShaderUniforms
|
||||
{
|
||||
NzLightUniforms uniforms;
|
||||
bool exists;
|
||||
int offset; // "Distance" entre Lights[0].type et Lights[1].type
|
||||
NzLightUniforms lightUniforms;
|
||||
bool hasLightUniforms;
|
||||
|
||||
/// Moins coûteux en mémoire que de stocker un NzLightUniforms par index de lumière,
|
||||
/// à voir si ça fonctionne chez tout le monde
|
||||
int lightOffset; // "Distance" entre Lights[0].type et Lights[1].type
|
||||
|
||||
// Autre uniformes
|
||||
int textureOverlay;
|
||||
};
|
||||
|
||||
mutable std::unordered_map<const NzShader*, LightUniforms> m_lightUniforms;
|
||||
mutable std::unordered_map<const NzShader*, ShaderUniforms> m_shaderUniforms;
|
||||
mutable NzForwardRenderQueue m_renderQueue;
|
||||
NzIndexBufferRef m_indexBuffer;
|
||||
mutable NzLightManager m_directionalLights;
|
||||
|
|
|
|||
|
|
@ -187,6 +187,7 @@ enum nzSamplerWrap
|
|||
|
||||
enum nzShaderUniform
|
||||
{
|
||||
///FIXME: Virer EyePosition, SceneAmbient et TargetSize de l'énumération (ils n'ont rien à faire dans le module de rendu)
|
||||
nzShaderUniform_EyePosition,
|
||||
nzShaderUniform_InvProjMatrix,
|
||||
nzShaderUniform_InvTargetSize,
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ void NzDeferredRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData
|
|||
}
|
||||
}
|
||||
|
||||
void NzDeferredRenderQueue::AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount)
|
||||
void NzDeferredRenderQueue::AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const NzTexture* overlay)
|
||||
{
|
||||
/*NzMaterial* material = sprite->GetMaterial();
|
||||
if (!material->IsLightingEnabled() || material->IsEnabled(nzRendererParameter_Blend))
|
||||
|
|
@ -111,7 +111,7 @@ void NzDeferredRenderQueue::AddSprites(const NzMaterial* material, const NzVerte
|
|||
else
|
||||
sprites[material].push_back(sprite);*/
|
||||
|
||||
m_forwardQueue->AddSprites(material, vertices, spriteCount);
|
||||
m_forwardQueue->AddSprites(material, vertices, spriteCount, overlay);
|
||||
}
|
||||
|
||||
void NzDeferredRenderQueue::Clear(bool fully)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace
|
|||
{
|
||||
ResourceType_IndexBuffer,
|
||||
ResourceType_Material,
|
||||
ResourceType_Texture,
|
||||
ResourceType_VertexBuffer
|
||||
};
|
||||
}
|
||||
|
|
@ -123,16 +124,25 @@ void NzForwardRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData&
|
|||
}
|
||||
}
|
||||
|
||||
void NzForwardRenderQueue::AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount)
|
||||
void NzForwardRenderQueue::AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const NzTexture* overlay)
|
||||
{
|
||||
BasicSpriteBatches::iterator it = basicSprites.find(material);
|
||||
if (it == basicSprites.end())
|
||||
auto matIt = basicSprites.find(material);
|
||||
if (matIt == basicSprites.end())
|
||||
{
|
||||
it = basicSprites.insert(std::make_pair(material, BasicSpriteBatches::mapped_type())).first;
|
||||
matIt = basicSprites.insert(std::make_pair(material, BasicSpriteBatches::mapped_type())).first;
|
||||
material->AddResourceListener(this, ResourceType_Material);
|
||||
}
|
||||
|
||||
auto& spriteVector = it->second;
|
||||
auto& overlayMap = matIt->second;
|
||||
auto overlayIt = overlayMap.find(overlay);
|
||||
if (overlayIt == overlayMap.end())
|
||||
{
|
||||
overlayIt = overlayMap.insert(std::make_pair(overlay, BasicSpriteOverlayContainer::mapped_type())).first;
|
||||
if (overlay)
|
||||
overlay->AddResourceListener(this, ResourceType_Texture);
|
||||
}
|
||||
|
||||
auto& spriteVector = overlayIt->second;
|
||||
spriteVector.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount}));
|
||||
}
|
||||
|
||||
|
|
@ -146,19 +156,26 @@ void NzForwardRenderQueue::Clear(bool fully)
|
|||
|
||||
if (fully)
|
||||
{
|
||||
for (auto& matIt : basicSprites)
|
||||
for (auto& matPair : basicSprites)
|
||||
{
|
||||
const NzMaterial* material = matIt.first;
|
||||
const NzMaterial* material = matPair.first;
|
||||
material->RemoveResourceListener(this);
|
||||
|
||||
auto& overlayMap = matPair.second;
|
||||
for (auto& overlayPair : overlayMap)
|
||||
{
|
||||
const NzTexture* overlay = overlayPair.first;
|
||||
overlay->RemoveResourceListener(this);
|
||||
}
|
||||
}
|
||||
basicSprites.clear();
|
||||
|
||||
for (auto& matIt : opaqueModels)
|
||||
for (auto& matPair : opaqueModels)
|
||||
{
|
||||
const NzMaterial* material = matIt.first;
|
||||
const NzMaterial* material = matPair.first;
|
||||
material->RemoveResourceListener(this);
|
||||
|
||||
MeshInstanceContainer& instances = std::get<2>(matIt.second);
|
||||
MeshInstanceContainer& instances = std::get<2>(matPair.second);
|
||||
for (auto& instanceIt : instances)
|
||||
{
|
||||
const NzMeshData& renderData = instanceIt.first;
|
||||
|
|
@ -284,6 +301,24 @@ void NzForwardRenderQueue::OnResourceReleased(const NzResource* resource, int in
|
|||
break;
|
||||
}
|
||||
|
||||
case ResourceType_Texture:
|
||||
{
|
||||
for (auto matIt = basicSprites.begin(); matIt != basicSprites.end(); ++matIt)
|
||||
{
|
||||
auto& overlayMap = matIt->second;
|
||||
for (auto overlayIt = overlayMap.begin(); overlayIt != overlayMap.end(); ++overlayIt)
|
||||
{
|
||||
if (overlayIt->first == resource)
|
||||
{
|
||||
overlayMap.erase(overlayIt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ResourceType_VertexBuffer:
|
||||
{
|
||||
for (auto& modelPair : opaqueModels)
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ void NzForwardRenderTechnique::DrawBasicSprites(const NzScene* scene) const
|
|||
{
|
||||
NzAbstractViewer* viewer = scene->GetViewer();
|
||||
const NzShader* lastShader = nullptr;
|
||||
const ShaderUniforms* shaderUniforms = nullptr;
|
||||
|
||||
NzRenderer::SetIndexBuffer(m_indexBuffer);
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Identity());
|
||||
|
|
@ -174,61 +175,84 @@ void NzForwardRenderTechnique::DrawBasicSprites(const NzScene* scene) const
|
|||
for (auto& matIt : m_renderQueue.basicSprites)
|
||||
{
|
||||
const NzMaterial* material = matIt.first;
|
||||
auto& spriteChainVector = matIt.second;
|
||||
auto& overlayMap = matIt.second;
|
||||
|
||||
unsigned int spriteChainCount = spriteChainVector.size();
|
||||
if (spriteChainCount > 0)
|
||||
for (auto& overlayIt : overlayMap)
|
||||
{
|
||||
// On commence par appliquer du matériau (et récupérer le shader ainsi activé)
|
||||
const NzShader* shader = material->Apply(nzShaderFlags_VertexColor);
|
||||
const NzTexture* overlay = overlayIt.first;
|
||||
auto& spriteChainVector = overlayIt.second;
|
||||
|
||||
// Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas
|
||||
if (shader != lastShader)
|
||||
unsigned int spriteChainCount = spriteChainVector.size();
|
||||
if (spriteChainCount > 0)
|
||||
{
|
||||
// Couleur ambiante de la scène
|
||||
shader->SendColor(shader->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
|
||||
// Position de la caméra
|
||||
shader->SendVector(shader->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition());
|
||||
// On commence par appliquer du matériau (et récupérer le shader ainsi activé)
|
||||
nzUInt32 flags = nzShaderFlags_VertexColor;
|
||||
if (overlay)
|
||||
flags |= nzShaderFlags_TextureOverlay;
|
||||
|
||||
lastShader = shader;
|
||||
}
|
||||
nzUInt8 overlayUnit;
|
||||
const NzShader* shader = material->Apply(flags, 0, &overlayUnit);
|
||||
|
||||
unsigned int spriteChain = 0; // Quelle chaîne de sprite traitons-nous
|
||||
unsigned int spriteChainOffset = 0; // À quel offset dans la dernière chaîne nous sommes-nous arrêtés
|
||||
|
||||
do
|
||||
{
|
||||
// On ouvre le buffer en écriture
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(m_spriteBuffer, nzBufferAccess_DiscardAndWrite);
|
||||
NzVertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<NzVertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
|
||||
|
||||
unsigned int spriteCount = 0;
|
||||
if (overlay)
|
||||
{
|
||||
NzForwardRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain];
|
||||
unsigned int count = std::min(s_maxSprites - spriteCount, currentChain.spriteCount - spriteChainOffset);
|
||||
|
||||
std::memcpy(vertices, currentChain.vertices + spriteChainOffset*4, 4*count*sizeof(NzVertexStruct_XYZ_Color_UV));
|
||||
vertices += count*4;
|
||||
|
||||
spriteCount += count;
|
||||
spriteChainOffset += count;
|
||||
|
||||
// Avons-nous traité la chaîne entière ?
|
||||
if (spriteChainOffset == currentChain.spriteCount)
|
||||
{
|
||||
spriteChain++;
|
||||
spriteChainOffset = 0;
|
||||
}
|
||||
overlayUnit++;
|
||||
NzRenderer::SetTexture(overlayUnit, overlay);
|
||||
NzRenderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler());
|
||||
}
|
||||
while (spriteCount < s_maxSprites && spriteChain < spriteChainCount);
|
||||
|
||||
vertexMapper.Unmap();
|
||||
// Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas
|
||||
if (shader != lastShader)
|
||||
{
|
||||
// Index des uniformes dans le shader
|
||||
shaderUniforms = GetShaderUniforms(shader);
|
||||
|
||||
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, spriteCount*6);
|
||||
// Couleur ambiante de la scène
|
||||
shader->SendColor(shader->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
|
||||
// Overlay
|
||||
shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit);
|
||||
// Position de la caméra
|
||||
shader->SendVector(shader->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition());
|
||||
|
||||
lastShader = shader;
|
||||
}
|
||||
|
||||
unsigned int spriteChain = 0; // Quelle chaîne de sprite traitons-nous
|
||||
unsigned int spriteChainOffset = 0; // À quel offset dans la dernière chaîne nous sommes-nous arrêtés
|
||||
|
||||
do
|
||||
{
|
||||
// On ouvre le buffer en écriture
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(m_spriteBuffer, nzBufferAccess_DiscardAndWrite);
|
||||
NzVertexStruct_XYZ_Color_UV* vertices = reinterpret_cast<NzVertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
|
||||
|
||||
unsigned int spriteCount = 0;
|
||||
{
|
||||
NzForwardRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain];
|
||||
unsigned int count = std::min(s_maxSprites - spriteCount, currentChain.spriteCount - spriteChainOffset);
|
||||
|
||||
std::memcpy(vertices, currentChain.vertices + spriteChainOffset*4, 4*count*sizeof(NzVertexStruct_XYZ_Color_UV));
|
||||
vertices += count*4;
|
||||
|
||||
spriteCount += count;
|
||||
spriteChainOffset += count;
|
||||
|
||||
// Avons-nous traité la chaîne entière ?
|
||||
if (spriteChainOffset == currentChain.spriteCount)
|
||||
{
|
||||
spriteChain++;
|
||||
spriteChainOffset = 0;
|
||||
}
|
||||
}
|
||||
while (spriteCount < s_maxSprites && spriteChain < spriteChainCount);
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, spriteCount*6);
|
||||
}
|
||||
while (spriteChain < spriteChainCount);
|
||||
|
||||
spriteChainVector.clear();
|
||||
}
|
||||
while (spriteChain < spriteChainCount);
|
||||
|
||||
spriteChainVector.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -236,8 +260,8 @@ void NzForwardRenderTechnique::DrawBasicSprites(const NzScene* scene) const
|
|||
void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
||||
{
|
||||
NzAbstractViewer* viewer = scene->GetViewer();
|
||||
const LightUniforms* lightUniforms = nullptr;
|
||||
const NzShader* lastShader = nullptr;
|
||||
const ShaderUniforms* shaderUniforms = nullptr;
|
||||
|
||||
for (auto& matIt : m_renderQueue.opaqueModels)
|
||||
{
|
||||
|
|
@ -262,14 +286,14 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
// Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas
|
||||
if (shader != lastShader)
|
||||
{
|
||||
// Index des uniformes dans le shader
|
||||
shaderUniforms = GetShaderUniforms(shader);
|
||||
|
||||
// Couleur ambiante de la scène
|
||||
shader->SendColor(shader->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
|
||||
// Position de la caméra
|
||||
shader->SendVector(shader->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition());
|
||||
|
||||
// Index des uniformes d'éclairage dans le shader
|
||||
lightUniforms = GetLightUniforms(shader);
|
||||
|
||||
lastShader = shader;
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +345,7 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
unsigned int passCount = (lightCount == 0) ? 1 : (lightCount-1)/NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1;
|
||||
for (unsigned int pass = 0; pass < passCount; ++pass)
|
||||
{
|
||||
if (lightUniforms->exists)
|
||||
if (shaderUniforms->hasLightUniforms)
|
||||
{
|
||||
unsigned int renderedLightCount = std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U));
|
||||
lightCount -= renderedLightCount;
|
||||
|
|
@ -338,10 +362,10 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
}
|
||||
|
||||
for (unsigned int i = 0; i < renderedLightCount; ++i)
|
||||
m_directionalLights.GetLight(lightIndex++)->Enable(shader, lightUniforms->uniforms, lightUniforms->offset*i);
|
||||
m_directionalLights.GetLight(lightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
|
||||
for (unsigned int i = renderedLightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
|
||||
NzLight::Disable(shader, lightUniforms->uniforms, lightUniforms->offset*i);
|
||||
NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
}
|
||||
|
||||
const NzMatrix4f* instanceMatrices = &instances[0];
|
||||
|
|
@ -369,7 +393,7 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
}
|
||||
else
|
||||
{
|
||||
if (lightUniforms->exists)
|
||||
if (shaderUniforms->hasLightUniforms)
|
||||
{
|
||||
for (const NzMatrix4f& matrix : instances)
|
||||
{
|
||||
|
|
@ -403,14 +427,14 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
for (unsigned int i = 0; i < renderedLightCount; ++i)
|
||||
{
|
||||
if (directionalLightIndex >= directionalLightCount)
|
||||
m_lights.GetResult(otherLightIndex++)->Enable(shader, lightUniforms->uniforms, lightUniforms->offset*i);
|
||||
m_lights.GetResult(otherLightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
else
|
||||
m_directionalLights.GetLight(directionalLightIndex++)->Enable(shader, lightUniforms->uniforms, lightUniforms->offset*i);
|
||||
m_directionalLights.GetLight(directionalLightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
}
|
||||
|
||||
// On désactive l'éventuel surplus
|
||||
for (unsigned int i = renderedLightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
|
||||
NzLight::Disable(shader, lightUniforms->uniforms, lightUniforms->offset*i);
|
||||
NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
|
||||
// Et on passe à l'affichage
|
||||
DrawFunc(meshData.primitiveMode, 0, indexCount);
|
||||
|
|
@ -438,8 +462,8 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
}
|
||||
|
||||
// Et on remet à zéro les données
|
||||
renderQueueInstancing = false;
|
||||
used = false;
|
||||
renderQueueInstancing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -447,8 +471,8 @@ void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
|
|||
void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene) const
|
||||
{
|
||||
NzAbstractViewer* viewer = scene->GetViewer();
|
||||
const LightUniforms* lightUniforms = nullptr;
|
||||
const NzShader* lastShader = nullptr;
|
||||
const ShaderUniforms* shaderUniforms = nullptr;
|
||||
unsigned int lightCount = 0;
|
||||
|
||||
for (unsigned int index : m_renderQueue.transparentModels)
|
||||
|
|
@ -464,18 +488,18 @@ void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene) const
|
|||
// Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas
|
||||
if (shader != lastShader)
|
||||
{
|
||||
// Index des uniformes dans le shader
|
||||
shaderUniforms = GetShaderUniforms(shader);
|
||||
|
||||
// Couleur ambiante de la scène
|
||||
shader->SendColor(shader->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor());
|
||||
// Position de la caméra
|
||||
shader->SendVector(shader->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition());
|
||||
|
||||
// Index des uniformes d'éclairage dans le shader
|
||||
lightUniforms = GetLightUniforms(shader);
|
||||
|
||||
// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
|
||||
lightCount = std::min(m_directionalLights.GetLightCount(), NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U));
|
||||
for (unsigned int i = 0; i < lightCount; ++i)
|
||||
m_directionalLights.GetLight(i)->Enable(shader, lightUniforms->uniforms, lightUniforms->offset*i);
|
||||
m_directionalLights.GetLight(i)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
|
||||
lastShader = shader;
|
||||
}
|
||||
|
|
@ -510,45 +534,46 @@ void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene) const
|
|||
{
|
||||
unsigned int count = std::min(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS - lightCount, m_lights.ComputeClosestLights(matrix.GetTranslation() + modelData.boundingSphere.GetPosition(), modelData.boundingSphere.radius, NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS));
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
m_lights.GetResult(i)->Enable(shader, lightUniforms->uniforms, lightUniforms->offset*(lightCount++));
|
||||
m_lights.GetResult(i)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*(lightCount++));
|
||||
}
|
||||
|
||||
for (unsigned int i = lightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i)
|
||||
NzLight::Disable(shader, lightUniforms->uniforms, lightUniforms->offset*i);
|
||||
NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, matrix);
|
||||
DrawFunc(meshData.primitiveMode, 0, indexCount);
|
||||
}
|
||||
}
|
||||
|
||||
const NzForwardRenderTechnique::LightUniforms* NzForwardRenderTechnique::GetLightUniforms(const NzShader* shader) const
|
||||
const NzForwardRenderTechnique::ShaderUniforms* NzForwardRenderTechnique::GetShaderUniforms(const NzShader* shader) const
|
||||
{
|
||||
auto it = m_lightUniforms.find(shader);
|
||||
if (it != m_lightUniforms.end())
|
||||
auto it = m_shaderUniforms.find(shader);
|
||||
if (it != m_shaderUniforms.end())
|
||||
return &(it->second);
|
||||
else
|
||||
{
|
||||
ShaderUniforms uniforms;
|
||||
uniforms.textureOverlay = shader->GetUniformLocation("TextureOverlay");
|
||||
|
||||
int type0Location = shader->GetUniformLocation("Lights[0].type");
|
||||
int type1Location = shader->GetUniformLocation("Lights[1].type");
|
||||
|
||||
LightUniforms lightUniforms;
|
||||
|
||||
if (type0Location > 0 && type1Location > 0)
|
||||
{
|
||||
lightUniforms.exists = true;
|
||||
lightUniforms.offset = type1Location - type0Location;
|
||||
lightUniforms.uniforms.ubo = false;
|
||||
lightUniforms.uniforms.locations.type = type0Location;
|
||||
lightUniforms.uniforms.locations.color = shader->GetUniformLocation("Lights[0].color");
|
||||
lightUniforms.uniforms.locations.factors = shader->GetUniformLocation("Lights[0].factors");
|
||||
lightUniforms.uniforms.locations.parameters1 = shader->GetUniformLocation("Lights[0].parameters1");
|
||||
lightUniforms.uniforms.locations.parameters2 = shader->GetUniformLocation("Lights[0].parameters2");
|
||||
lightUniforms.uniforms.locations.parameters3 = shader->GetUniformLocation("Lights[0].parameters3");
|
||||
uniforms.hasLightUniforms = true;
|
||||
uniforms.lightOffset = type1Location - type0Location;
|
||||
uniforms.lightUniforms.ubo = false;
|
||||
uniforms.lightUniforms.locations.type = type0Location;
|
||||
uniforms.lightUniforms.locations.color = shader->GetUniformLocation("Lights[0].color");
|
||||
uniforms.lightUniforms.locations.factors = shader->GetUniformLocation("Lights[0].factors");
|
||||
uniforms.lightUniforms.locations.parameters1 = shader->GetUniformLocation("Lights[0].parameters1");
|
||||
uniforms.lightUniforms.locations.parameters2 = shader->GetUniformLocation("Lights[0].parameters2");
|
||||
uniforms.lightUniforms.locations.parameters3 = shader->GetUniformLocation("Lights[0].parameters3");
|
||||
}
|
||||
else
|
||||
lightUniforms.exists = false;
|
||||
uniforms.hasLightUniforms = false;
|
||||
|
||||
auto pair = m_lightUniforms.emplace(shader, lightUniforms);
|
||||
auto pair = m_shaderUniforms.emplace(shader, uniforms);
|
||||
return &(pair.first->second);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -687,11 +687,13 @@ void NzMaterial::GenerateShader(nzUInt32 flags) const
|
|||
list.SetParameter("PARALLAX_MAPPING", m_heightMap.IsValid());
|
||||
list.SetParameter("SPECULAR_MAPPING", m_specularMap.IsValid());
|
||||
list.SetParameter("TEXTURE_MAPPING", m_alphaMap.IsValid() || m_diffuseMap.IsValid() || m_emissiveMap.IsValid() ||
|
||||
m_normalMap.IsValid() || m_heightMap.IsValid() || m_specularMap.IsValid());
|
||||
m_normalMap.IsValid() || m_heightMap.IsValid() || m_specularMap.IsValid() ||
|
||||
flags & nzShaderFlags_TextureOverlay);
|
||||
list.SetParameter("TRANSFORM", m_transformEnabled);
|
||||
|
||||
list.SetParameter("FLAG_DEFERRED", static_cast<bool>((flags & nzShaderFlags_Deferred) != 0));
|
||||
list.SetParameter("FLAG_INSTANCING", static_cast<bool>((flags & nzShaderFlags_Instancing) != 0));
|
||||
list.SetParameter("FLAG_TEXTUREOVERLAY", static_cast<bool>((flags & nzShaderFlags_TextureOverlay) != 0));
|
||||
list.SetParameter("FLAG_VERTEXCOLOR", static_cast<bool>((flags & nzShaderFlags_VertexColor) != 0));
|
||||
|
||||
ShaderInstance& instance = m_shaders[flags];
|
||||
|
|
@ -759,7 +761,7 @@ bool NzMaterial::Initialize()
|
|||
vertexShader.Set(reinterpret_cast<const char*>(compatibilityVertexShader), sizeof(compatibilityVertexShader));
|
||||
}
|
||||
|
||||
uberShader->SetShader(nzShaderStage_Fragment, fragmentShader, "ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING");
|
||||
uberShader->SetShader(nzShaderStage_Fragment, fragmentShader, "FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST AUTO_TEXCOORDS DIFFUSE_MAPPING");
|
||||
uberShader->SetShader(nzShaderStage_Vertex, vertexShader, "FLAG_INSTANCING FLAG_VERTEXCOLOR TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
|
||||
|
||||
NzUberShaderLibrary::Register("Basic", uberShader.get());
|
||||
|
|
@ -800,7 +802,7 @@ bool NzMaterial::Initialize()
|
|||
vertexShader.Set(reinterpret_cast<const char*>(compatibilityVertexShader), sizeof(compatibilityVertexShader));
|
||||
}
|
||||
|
||||
uberShader->SetShader(nzShaderStage_Fragment, fragmentShader, "FLAG_DEFERRED ALPHA_MAPPING ALPHA_TEST DIFFUSE_MAPPING EMISSIVE_MAPPING LIGHTING NORMAL_MAPPING PARALLAX_MAPPING SPECULAR_MAPPING");
|
||||
uberShader->SetShader(nzShaderStage_Fragment, fragmentShader, "FLAG_DEFERRED FLAG_TEXTUREOVERLAY ALPHA_MAPPING ALPHA_TEST DIFFUSE_MAPPING EMISSIVE_MAPPING LIGHTING NORMAL_MAPPING PARALLAX_MAPPING SPECULAR_MAPPING");
|
||||
uberShader->SetShader(nzShaderStage_Vertex, vertexShader, "FLAG_DEFERRED FLAG_INSTANCING FLAG_VERTEXCOLOR COMPUTE_TBNMATRIX LIGHTING PARALLAX_MAPPING TEXTURE_MAPPING TRANSFORM UNIFORM_VERTEX_DEPTH");
|
||||
|
||||
NzUberShaderLibrary::Register("PhongLighting", uberShader.get());
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ in vec2 vTexCoord;
|
|||
out vec4 RenderTarget0;
|
||||
|
||||
/********************Uniformes********************/
|
||||
uniform vec2 InvTargetSize;
|
||||
uniform sampler2D MaterialAlphaMap;
|
||||
uniform float MaterialAlphaThreshold;
|
||||
uniform vec4 MaterialDiffuse;
|
||||
uniform sampler2D MaterialDiffuseMap;
|
||||
uniform vec2 InvTargetSize;
|
||||
uniform sampler2D TextureOverlay;
|
||||
|
||||
/********************Fonctions********************/
|
||||
void main()
|
||||
|
|
@ -35,6 +36,10 @@ void main()
|
|||
fragmentColor.a *= texture(MaterialAlphaMap, texCoord).r;
|
||||
#endif
|
||||
|
||||
#if FLAG_TEXTUREOVERLAY
|
||||
fragmentColor *= texture(TextureOverlay, texCoord);
|
||||
#endif
|
||||
|
||||
#if ALPHA_TEST
|
||||
if (fragmentColor.a < MaterialAlphaThreshold)
|
||||
discard;
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
35,105,102,32,69,65,82,76,89,95,70,82,65,71,77,69,78,84,95,84,69,83,84,83,32,38,38,32,33,65,76,80,72,65,95,84,69,83,84,13,10,108,97,121,111,117,116,40,101,97,114,108,121,95,102,114,97,103,109,101,110,116,95,116,101,115,116,115,41,32,105,110,59,13,10,35,101,110,100,105,102,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,69,110,116,114,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,105,110,32,118,101,99,52,32,118,67,111,108,111,114,59,13,10,105,110,32,118,101,99,50,32,118,84,101,120,67,111,111,114,100,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,83,111,114,116,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,48,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,85,110,105,102,111,114,109,101,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,59,13,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,59,13,10,117,110,105,102,111,114,109,32,118,101,99,52,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,59,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,59,13,10,117,110,105,102,111,114,109,32,118,101,99,50,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,70,111,110,99,116,105,111,110,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,118,111,105,100,32,109,97,105,110,40,41,13,10,123,13,10,9,118,101,99,52,32,102,114,97,103,109,101,110,116,67,111,108,111,114,32,61,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,32,42,32,118,67,111,108,111,114,59,13,10,13,10,35,105,102,32,65,85,84,79,95,84,69,88,67,79,79,82,68,83,13,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,103,108,95,70,114,97,103,67,111,111,114,100,46,120,121,32,42,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,13,10,35,101,108,115,101,13,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,118,84,101,120,67,111,111,114,100,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,68,73,70,70,85,83,69,95,77,65,80,80,73,78,71,13,10,9,102,114,97,103,109,101,110,116,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,44,32,116,101,120,67,111,111,114,100,41,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,65,76,80,72,65,95,77,65,80,80,73,78,71,13,10,9,102,114,97,103,109,101,110,116,67,111,108,111,114,46,97,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,65,76,80,72,65,95,84,69,83,84,13,10,9,105,102,32,40,102,114,97,103,109,101,110,116,67,111,108,111,114,46,97,32,60,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,41,13,10,9,9,100,105,115,99,97,114,100,59,13,10,35,101,110,100,105,102,13,10,13,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,102,114,97,103,109,101,110,116,67,111,108,111,114,59,13,10,125,
|
||||
35,105,102,32,69,65,82,76,89,95,70,82,65,71,77,69,78,84,95,84,69,83,84,83,32,38,38,32,33,65,76,80,72,65,95,84,69,83,84,13,10,108,97,121,111,117,116,40,101,97,114,108,121,95,102,114,97,103,109,101,110,116,95,116,101,115,116,115,41,32,105,110,59,13,10,35,101,110,100,105,102,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,69,110,116,114,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,105,110,32,118,101,99,52,32,118,67,111,108,111,114,59,13,10,105,110,32,118,101,99,50,32,118,84,101,120,67,111,111,114,100,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,83,111,114,116,97,110,116,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,111,117,116,32,118,101,99,52,32,82,101,110,100,101,114,84,97,114,103,101,116,48,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,85,110,105,102,111,114,109,101,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,117,110,105,102,111,114,109,32,118,101,99,50,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,59,13,10,117,110,105,102,111,114,109,32,102,108,111,97,116,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,59,13,10,117,110,105,102,111,114,109,32,118,101,99,52,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,59,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,59,13,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,84,101,120,116,117,114,101,79,118,101,114,108,97,121,59,13,10,13,10,47,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,70,111,110,99,116,105,111,110,115,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,47,13,10,118,111,105,100,32,109,97,105,110,40,41,13,10,123,13,10,9,118,101,99,52,32,102,114,97,103,109,101,110,116,67,111,108,111,114,32,61,32,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,32,42,32,118,67,111,108,111,114,59,13,10,13,10,35,105,102,32,65,85,84,79,95,84,69,88,67,79,79,82,68,83,13,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,103,108,95,70,114,97,103,67,111,111,114,100,46,120,121,32,42,32,73,110,118,84,97,114,103,101,116,83,105,122,101,59,13,10,35,101,108,115,101,13,10,9,118,101,99,50,32,116,101,120,67,111,111,114,100,32,61,32,118,84,101,120,67,111,111,114,100,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,68,73,70,70,85,83,69,95,77,65,80,80,73,78,71,13,10,9,102,114,97,103,109,101,110,116,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,68,105,102,102,117,115,101,77,97,112,44,32,116,101,120,67,111,111,114,100,41,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,65,76,80,72,65,95,77,65,80,80,73,78,71,13,10,9,102,114,97,103,109,101,110,116,67,111,108,111,114,46,97,32,42,61,32,116,101,120,116,117,114,101,40,77,97,116,101,114,105,97,108,65,108,112,104,97,77,97,112,44,32,116,101,120,67,111,111,114,100,41,46,114,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,70,76,65,71,95,84,69,88,84,85,82,69,79,86,69,82,76,65,89,13,10,9,102,114,97,103,109,101,110,116,67,111,108,111,114,32,42,61,32,116,101,120,116,117,114,101,40,84,101,120,116,117,114,101,79,118,101,114,108,97,121,44,32,116,101,120,67,111,111,114,100,41,59,13,10,35,101,110,100,105,102,13,10,13,10,35,105,102,32,65,76,80,72,65,95,84,69,83,84,13,10,9,105,102,32,40,102,114,97,103,109,101,110,116,67,111,108,111,114,46,97,32,60,32,77,97,116,101,114,105,97,108,65,108,112,104,97,84,104,114,101,115,104,111,108,100,41,13,10,9,9,100,105,115,99,97,114,100,59,13,10,35,101,110,100,105,102,13,10,13,10,9,82,101,110,100,101,114,84,97,114,103,101,116,48,32,61,32,102,114,97,103,109,101,110,116,67,111,108,111,114,59,13,10,125,
|
||||
|
|
@ -50,6 +50,8 @@ uniform float ParallaxBias = -0.03;
|
|||
uniform float ParallaxScale = 0.02;
|
||||
uniform vec4 SceneAmbient;
|
||||
|
||||
uniform sampler2D TextureOverlay;
|
||||
|
||||
/********************Fonctions********************/
|
||||
vec3 FloatToColor(float f)
|
||||
{
|
||||
|
|
@ -91,6 +93,10 @@ void main()
|
|||
diffuseColor *= texture(MaterialDiffuseMap, texCoord);
|
||||
#endif
|
||||
|
||||
#if FLAG_TEXTUREOVERLAY
|
||||
diffuseColor *= texture(TextureOverlay, texCoord);
|
||||
#endif
|
||||
|
||||
#if FLAG_DEFERRED
|
||||
#if ALPHA_TEST
|
||||
#if ALPHA_MAPPING // Inutile de faire de l'alpha-mapping sans alpha-test en Deferred (l'alpha n'est pas sauvegardé)
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue