Пример #1
0
	SubMesh* Mesh::BuildSubMesh(const Primitive& primitive, const MeshParams& params)
	{
		#if NAZARA_UTILITY_SAFE
		if (!m_impl)
		{
			NazaraError("Mesh not created");
			return nullptr;
		}

		if (m_impl->animationType != AnimationType_Static)
		{
			NazaraError("Mesh must be static");
			return nullptr;
		}

		if (!params.IsValid())
		{
			NazaraError("Parameters must be valid");
			return nullptr;
		}
		#endif

		Boxf aabb;
		IndexBufferRef indexBuffer;
		VertexBufferRef vertexBuffer;

		Matrix4f matrix(primitive.matrix);
		matrix.ApplyScale(params.scale);

		VertexDeclaration* declaration = VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent);

		switch (primitive.type)
		{
			case PrimitiveType_Box:
			{
				unsigned int indexCount;
				unsigned int vertexCount;
				ComputeBoxIndexVertexCount(primitive.box.subdivision, &indexCount, &vertexCount);

				indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), indexCount, params.storage, BufferUsage_Static);
				vertexBuffer = VertexBuffer::New(declaration, vertexCount, params.storage, BufferUsage_Static);

				VertexMapper vertexMapper(vertexBuffer, BufferAccess_WriteOnly);

				VertexPointers pointers;
				pointers.normalPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Normal);
				pointers.positionPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
				pointers.tangentPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Tangent);
				pointers.uvPtr = vertexMapper.GetComponentPtr<Vector2f>(VertexComponent_TexCoord);

				IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly);
				GenerateBox(primitive.box.lengths, primitive.box.subdivision, matrix, primitive.textureCoords, pointers, indexMapper.begin(), &aabb);
				break;
			}

			case PrimitiveType_Cone:
			{
				unsigned int indexCount;
				unsigned int vertexCount;
				ComputeConeIndexVertexCount(primitive.cone.subdivision, &indexCount, &vertexCount);

				indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), indexCount, params.storage, BufferUsage_Static);
				vertexBuffer = VertexBuffer::New(declaration, vertexCount, params.storage, BufferUsage_Static);

				VertexMapper vertexMapper(vertexBuffer, BufferAccess_WriteOnly);

				VertexPointers pointers;
				pointers.normalPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Normal);
				pointers.positionPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
				pointers.tangentPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Tangent);
				pointers.uvPtr = vertexMapper.GetComponentPtr<Vector2f>(VertexComponent_TexCoord);

				IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly);
				GenerateCone(primitive.cone.length, primitive.cone.radius, primitive.cone.subdivision, matrix, primitive.textureCoords, pointers, indexMapper.begin(), &aabb);
				break;
			}

			case PrimitiveType_Plane:
			{
				unsigned int indexCount;
				unsigned int vertexCount;
				ComputePlaneIndexVertexCount(primitive.plane.subdivision, &indexCount, &vertexCount);

				indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), indexCount, params.storage, BufferUsage_Static);
				vertexBuffer = VertexBuffer::New(declaration, vertexCount, params.storage, BufferUsage_Static);

				VertexMapper vertexMapper(vertexBuffer, BufferAccess_WriteOnly);

				VertexPointers pointers;
				pointers.normalPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Normal);
				pointers.positionPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
				pointers.tangentPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Tangent);
				pointers.uvPtr = vertexMapper.GetComponentPtr<Vector2f>(VertexComponent_TexCoord);

				IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly);
				GeneratePlane(primitive.plane.subdivision, primitive.plane.size, matrix, primitive.textureCoords, pointers, indexMapper.begin(), &aabb);
				break;
			}

			case PrimitiveType_Sphere:
			{
				switch (primitive.sphere.type)
				{
					case SphereType_Cubic:
					{
						unsigned int indexCount;
						unsigned int vertexCount;
						ComputeCubicSphereIndexVertexCount(primitive.sphere.cubic.subdivision, &indexCount, &vertexCount);

						indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), indexCount, params.storage, BufferUsage_Static);
						vertexBuffer = VertexBuffer::New(declaration, vertexCount, params.storage, BufferUsage_Static);

						VertexMapper vertexMapper(vertexBuffer, BufferAccess_ReadWrite);

						VertexPointers pointers;
						pointers.normalPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Normal);
						pointers.positionPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
						pointers.tangentPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Tangent);
						pointers.uvPtr = vertexMapper.GetComponentPtr<Vector2f>(VertexComponent_TexCoord);

						IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly);
						GenerateCubicSphere(primitive.sphere.size, primitive.sphere.cubic.subdivision, matrix, primitive.textureCoords, pointers, indexMapper.begin(), &aabb);
						break;
					}

					case SphereType_Ico:
					{
						unsigned int indexCount;
						unsigned int vertexCount;
						ComputeIcoSphereIndexVertexCount(primitive.sphere.ico.recursionLevel, &indexCount, &vertexCount);

						indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), indexCount, params.storage, BufferUsage_Static);
						vertexBuffer = VertexBuffer::New(declaration, vertexCount, params.storage, BufferUsage_Static);

						VertexMapper vertexMapper(vertexBuffer, BufferAccess_WriteOnly);

						VertexPointers pointers;
						pointers.normalPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Normal);
						pointers.positionPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
						pointers.tangentPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Tangent);
						pointers.uvPtr = vertexMapper.GetComponentPtr<Vector2f>(VertexComponent_TexCoord);

						IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly);
						GenerateIcoSphere(primitive.sphere.size, primitive.sphere.ico.recursionLevel, matrix, primitive.textureCoords, pointers, indexMapper.begin(), &aabb);
						break;
					}

					case SphereType_UV:
					{
						unsigned int indexCount;
						unsigned int vertexCount;
						ComputeUvSphereIndexVertexCount(primitive.sphere.uv.sliceCount, primitive.sphere.uv.stackCount, &indexCount, &vertexCount);

						indexBuffer = IndexBuffer::New(vertexCount > std::numeric_limits<UInt16>::max(), indexCount, params.storage, BufferUsage_Static);
						vertexBuffer = VertexBuffer::New(declaration, vertexCount, params.storage, BufferUsage_Static);

						VertexMapper vertexMapper(vertexBuffer, BufferAccess_WriteOnly);

						VertexPointers pointers;
						pointers.normalPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Normal);
						pointers.positionPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Position);
						pointers.tangentPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent_Tangent);
						pointers.uvPtr = vertexMapper.GetComponentPtr<Vector2f>(VertexComponent_TexCoord);

						IndexMapper indexMapper(indexBuffer, BufferAccess_WriteOnly);
						GenerateUvSphere(primitive.sphere.size, primitive.sphere.uv.sliceCount, primitive.sphere.uv.stackCount, matrix, primitive.textureCoords, pointers, indexMapper.begin(), &aabb);
						break;
					}
				}
				break;
			}
		}

		StaticMeshRef subMesh = StaticMesh::New(this);
		if (!subMesh->Create(vertexBuffer))
		{
			NazaraError("Failed to create StaticMesh");
			return nullptr;
		}

		if (params.optimizeIndexBuffers)
			indexBuffer->Optimize();

		subMesh->SetAABB(aabb);
		subMesh->SetIndexBuffer(indexBuffer);

		AddSubMesh(subMesh);
		return subMesh;
	}
void NzForwardRenderTechnique::DrawBillboards(const NzScene* scene) const
{
	NzAbstractViewer* viewer = scene->GetViewer();
	const NzShader* lastShader = nullptr;
	const ShaderUniforms* shaderUniforms = nullptr;

	if (NzRenderer::HasCapability(nzRendererCap_Instancing))
	{
		NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer();
		instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration);

		NzRenderer::SetVertexBuffer(&s_quadVertexBuffer);

		for (auto& matIt : m_renderQueue.billboards)
		{
			const NzMaterial* material = matIt.first;
			auto& entry = matIt.second;
			auto& billboardVector = entry.billboards;

			unsigned int billboardCount = billboardVector.size();
			if (billboardCount > 0)
			{
				// On commence par appliquer du matériau (et récupérer le shader ainsi activé)
				const NzShader* shader = material->Apply(nzShaderFlags_Billboard | nzShaderFlags_Instancing | nzShaderFlags_VertexColor);

				// 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(shaderUniforms->sceneAmbient, scene->GetAmbientColor());
					// Position de la caméra
					shader->SendVector(shaderUniforms->eyePosition, viewer->GetEyePosition());

					lastShader = shader;
				}

				const NzForwardRenderQueue::BillboardData* data = &billboardVector[0];
				unsigned int maxBillboardPerDraw = instanceBuffer->GetVertexCount();
				do
				{
					unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw);
					billboardCount -= renderedBillboardCount;

					instanceBuffer->Fill(data, 0, renderedBillboardCount, true);
					data += renderedBillboardCount;

					NzRenderer::DrawPrimitivesInstanced(renderedBillboardCount, nzPrimitiveMode_TriangleStrip, 0, 4);
				}
				while (billboardCount > 0);

				billboardVector.clear();
			}
		}
	}
	else
	{
		NzRenderer::SetIndexBuffer(&s_quadIndexBuffer);
		NzRenderer::SetVertexBuffer(&m_billboardPointBuffer);

		for (auto& matIt : m_renderQueue.billboards)
		{
			const NzMaterial* material = matIt.first;
			auto& entry = matIt.second;
			auto& billboardVector = entry.billboards;

			unsigned int billboardCount = billboardVector.size();
			if (billboardCount > 0)
			{
				// On commence par appliquer du matériau (et récupérer le shader ainsi activé)
				const NzShader* shader = material->Apply(nzShaderFlags_Billboard | nzShaderFlags_VertexColor);

				// Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas
				if (shader != lastShader)
				{
					// Couleur ambiante de la scène
					shader->SendColor(shaderUniforms->sceneAmbient, scene->GetAmbientColor());
					// Position de la caméra
					shader->SendVector(shaderUniforms->eyePosition, viewer->GetEyePosition());

					lastShader = shader;
				}

				const NzForwardRenderQueue::BillboardData* data = &billboardVector[0];
				unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount()/4);

				do
				{
					unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw);
					billboardCount -= renderedBillboardCount;

					NzBufferMapper<NzVertexBuffer> vertexMapper(m_billboardPointBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedBillboardCount*4);
					BillboardPoint* vertices = reinterpret_cast<BillboardPoint*>(vertexMapper.GetPointer());

					for (unsigned int i = 0; i < renderedBillboardCount; ++i)
					{
						const NzForwardRenderQueue::BillboardData& billboard = *data++;

						vertices->color = billboard.color;
						vertices->position = billboard.center;
						vertices->sinCos = billboard.sinCos;
						vertices->size = billboard.size;
						vertices->uv.Set(0.f, 1.f);
						vertices++;

						vertices->color = billboard.color;
						vertices->position = billboard.center;
						vertices->sinCos = billboard.sinCos;
						vertices->size = billboard.size;
						vertices->uv.Set(1.f, 1.f);
						vertices++;

						vertices->color = billboard.color;
						vertices->position = billboard.center;
						vertices->sinCos = billboard.sinCos;
						vertices->size = billboard.size;
						vertices->uv.Set(0.f, 0.f);
						vertices++;

						vertices->color = billboard.color;
						vertices->position = billboard.center;
						vertices->sinCos = billboard.sinCos;
						vertices->size = billboard.size;
						vertices->uv.Set(1.f, 0.f);
						vertices++;
					}

					vertexMapper.Unmap();

					NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, renderedBillboardCount*6);
				}
				while (billboardCount > 0);

				billboardVector.clear();
			}
		}
	}
}
Пример #3
0
NzSubMesh* NzMesh::BuildSubMesh(const NzPrimitive& primitive, const NzMeshParams& params)
{
	#if NAZARA_UTILITY_SAFE
	if (!m_impl)
	{
		NazaraError("Mesh not created");
		return nullptr;
	}

	if (m_impl->animationType != nzAnimationType_Static)
	{
		NazaraError("Mesh must be static");
		return nullptr;
	}

	if (!params.IsValid())
	{
		NazaraError("Parameters must be valid");
		return nullptr;
	}
	#endif

	NzBoxf aabb;
	std::unique_ptr<NzIndexBuffer> indexBuffer;
	std::unique_ptr<NzVertexBuffer> vertexBuffer;

	NzMatrix4f matrix(primitive.matrix);
	matrix.ApplyScale(params.scale);

	NzVertexDeclaration* declaration = NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent);

	switch (primitive.type)
	{
		case nzPrimitiveType_Box:
		{
			unsigned int indexCount;
			unsigned int vertexCount;
			NzComputeBoxIndexVertexCount(primitive.box.subdivision, &indexCount, &vertexCount);

			indexBuffer.reset(new NzIndexBuffer(vertexCount > std::numeric_limits<nzUInt16>::max(), indexCount, params.storage, nzBufferUsage_Static));
			indexBuffer->SetPersistent(false);

			vertexBuffer.reset(new NzVertexBuffer(declaration, vertexCount, params.storage, nzBufferUsage_Static));
			vertexBuffer->SetPersistent(false);

			NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
			NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_WriteOnly);

			NzGenerateBox(primitive.box.lengths, primitive.box.subdivision, matrix, primitive.textureCoords, static_cast<NzMeshVertex*>(vertexMapper.GetPointer()), indexMapper.begin(), &aabb);
			break;
		}

		case nzPrimitiveType_Cone:
		{
			unsigned int indexCount;
			unsigned int vertexCount;
			NzComputeConeIndexVertexCount(primitive.cone.subdivision, &indexCount, &vertexCount);

			indexBuffer.reset(new NzIndexBuffer(vertexCount > std::numeric_limits<nzUInt16>::max(), indexCount, params.storage, nzBufferUsage_Static));
			indexBuffer->SetPersistent(false);

			vertexBuffer.reset(new NzVertexBuffer(declaration, vertexCount, params.storage, nzBufferUsage_Static));
			vertexBuffer->SetPersistent(false);

			NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
			NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_WriteOnly);

			NzGenerateCone(primitive.cone.length, primitive.cone.radius, primitive.cone.subdivision, matrix, primitive.textureCoords, static_cast<NzMeshVertex*>(vertexMapper.GetPointer()), indexMapper.begin(), &aabb);
			break;
		}

		case nzPrimitiveType_Plane:
		{
			unsigned int indexCount;
			unsigned int vertexCount;
			NzComputePlaneIndexVertexCount(primitive.plane.subdivision, &indexCount, &vertexCount);

			indexBuffer.reset(new NzIndexBuffer(vertexCount > std::numeric_limits<nzUInt16>::max(), indexCount, params.storage, nzBufferUsage_Static));
			indexBuffer->SetPersistent(false);

			vertexBuffer.reset(new NzVertexBuffer(declaration, vertexCount, params.storage, nzBufferUsage_Static));
			vertexBuffer->SetPersistent(false);

			NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
			NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_WriteOnly);

			NzGeneratePlane(primitive.plane.subdivision, primitive.plane.size, matrix, primitive.textureCoords, static_cast<NzMeshVertex*>(vertexMapper.GetPointer()), indexMapper.begin(), &aabb);
			break;
		}

		case nzPrimitiveType_Sphere:
		{
			switch (primitive.sphere.type)
			{
				case nzSphereType_Cubic:
				{
					unsigned int indexCount;
					unsigned int vertexCount;
					NzComputeCubicSphereIndexVertexCount(primitive.sphere.cubic.subdivision, &indexCount, &vertexCount);

					indexBuffer.reset(new NzIndexBuffer(vertexCount > std::numeric_limits<nzUInt16>::max(), indexCount, params.storage, nzBufferUsage_Static));
					indexBuffer->SetPersistent(false);

					vertexBuffer.reset(new NzVertexBuffer(declaration, vertexCount, params.storage, nzBufferUsage_Static));
					vertexBuffer->SetPersistent(false);

					NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
					NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_WriteOnly);

					NzGenerateCubicSphere(primitive.sphere.size, primitive.sphere.cubic.subdivision, matrix, primitive.textureCoords, static_cast<NzMeshVertex*>(vertexMapper.GetPointer()), indexMapper.begin(), &aabb);
					break;
				}

				case nzSphereType_Ico:
				{
					unsigned int indexCount;
					unsigned int vertexCount;
					NzComputeIcoSphereIndexVertexCount(primitive.sphere.ico.recursionLevel, &indexCount, &vertexCount);

					indexBuffer.reset(new NzIndexBuffer(vertexCount > std::numeric_limits<nzUInt16>::max(), indexCount, params.storage, nzBufferUsage_Static));
					indexBuffer->SetPersistent(false);

					vertexBuffer.reset(new NzVertexBuffer(declaration, vertexCount, params.storage, nzBufferUsage_Static));
					vertexBuffer->SetPersistent(false);

					NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
					NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_WriteOnly);

					NzGenerateIcoSphere(primitive.sphere.size, primitive.sphere.ico.recursionLevel, matrix, primitive.textureCoords, static_cast<NzMeshVertex*>(vertexMapper.GetPointer()), indexMapper.begin(), &aabb);
					break;
				}

				case nzSphereType_UV:
				{
					unsigned int indexCount;
					unsigned int vertexCount;
					NzComputeUvSphereIndexVertexCount(primitive.sphere.uv.sliceCount, primitive.sphere.uv.stackCount, &indexCount, &vertexCount);

					indexBuffer.reset(new NzIndexBuffer(vertexCount > std::numeric_limits<nzUInt16>::max(), indexCount, params.storage, nzBufferUsage_Static));
					indexBuffer->SetPersistent(false);

					vertexBuffer.reset(new NzVertexBuffer(declaration, vertexCount, params.storage, nzBufferUsage_Static));
					vertexBuffer->SetPersistent(false);

					NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
					NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_WriteOnly);

					NzGenerateUvSphere(primitive.sphere.size, primitive.sphere.uv.sliceCount, primitive.sphere.uv.stackCount, matrix, primitive.textureCoords, static_cast<NzMeshVertex*>(vertexMapper.GetPointer()), indexMapper.begin(), &aabb);
					break;
				}
			}
			break;
		}
	}

	std::unique_ptr<NzStaticMesh> subMesh(new NzStaticMesh(this));
	if (!subMesh->Create(vertexBuffer.get()))
	{
		NazaraError("Failed to create StaticMesh");
		return nullptr;
	}
	vertexBuffer.release();

	if (params.optimizeIndexBuffers)
		indexBuffer->Optimize();

	subMesh->SetIndexBuffer(indexBuffer.get());
	indexBuffer.release();

	subMesh->SetAABB(aabb);
	AddSubMesh(subMesh.get());

	return subMesh.release();
}
void NzForwardRenderTechnique::DrawBasicSprites(const NzScene* scene) const
{
	NzAbstractViewer* viewer = scene->GetViewer();
	const NzShader* lastShader = nullptr;
	const ShaderUniforms* shaderUniforms = nullptr;

	NzRenderer::SetIndexBuffer(&s_quadIndexBuffer);
	NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Identity());
	NzRenderer::SetVertexBuffer(&m_spriteBuffer);

	for (auto& matIt : m_renderQueue.basicSprites)
	{
		const NzMaterial* material = matIt.first;
		auto& matEntry = matIt.second;

		if (matEntry.enabled)
		{
			auto& overlayMap = matEntry.overlayMap;
			for (auto& overlayIt : overlayMap)
			{
				const NzTexture* overlay = overlayIt.first;
				auto& spriteChainVector = overlayIt.second.spriteChains;

				unsigned int spriteChainCount = spriteChainVector.size();
				if (spriteChainCount > 0)
				{
					// On commence par appliquer du matériau (et récupérer le shader ainsi activé)
					nzUInt32 flags = nzShaderFlags_VertexColor;
					if (overlay)
						flags |= nzShaderFlags_TextureOverlay;

					nzUInt8 overlayUnit;
					const NzShader* shader = material->Apply(flags, 0, &overlayUnit);

					if (overlay)
					{
						overlayUnit++;
						NzRenderer::SetTexture(overlayUnit, overlay);
						NzRenderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler());
					}

					// 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(shaderUniforms->sceneAmbient, scene->GetAmbientColor());
						// Overlay
						shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit);
						// Position de la caméra
						shader->SendVector(shaderUniforms->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;
						unsigned int maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount()/4);

						do
						{
							NzForwardRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain];
							unsigned int count = std::min(maxSpriteCount - 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 < maxSpriteCount && spriteChain < spriteChainCount);

						vertexMapper.Unmap();

						NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, spriteCount*6);
					}
					while (spriteChain < spriteChainCount);

					spriteChainVector.clear();
				}
			}

			// On remet à zéro
			matEntry.enabled = false;
		}
	}
}