Exemplo n.º 1
0
bool
Moose::Graphics::CRenderState::Prepare()
{
    bool bRetval = true;
    CShader & s = **GetShaderHandle();
    if ( GetShaderHandle().IsNull() == false )
    {
        GetShaderAttribs().Bind( s);
        bRetval = (*GetShaderHandle())->Link();
        GetShaderUniforms().Bind( s);
    
        for(int i=0;i<NUM_LIGHTS;i++)
        {
            m_Lights.diffuse[i].Bind(s,0);
            m_Lights.ambient[i].Bind(s,0) ;
            m_Lights.specular[i].Bind(s,0);
            m_Lights.position[i].Bind(s,0);
            m_Lights.halfVector[i].Bind(s,0);
            m_Lights.direction[i].Bind(s,0);
            m_Lights.spotAngle[i].Bind(s,0);
            m_Lights.spotExponent[i].Bind(s,0);
            m_Lights.constantAttenuation[i].Bind(s,0);
            m_Lights.linearAttenuation[i].Bind(s,0);
            m_Lights.quadraticAttenuation[i].Bind(s,0);
            m_Lights.enabled[i].Bind(s,0);
        }
        m_GlobalAmbient.Bind(s,0);
        m_Material.Bind(s,0);

        m_ViewUniform.SetName("m_viewMatrix");
        m_ViewUniform.Bind( s, 0);  // index 0 is not used in uniform
        m_ProjUniform.SetName("m_projMatrix");
        m_ProjUniform.Bind( s, 0);  // index 0 is not used in uniform
        m_ModelUniform.SetName("m_modelMatrix");
        m_ModelUniform.Bind( s, 0 ); // index 0 is not used in uniform
        
    }
    return bRetval;
}
void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene) const
{
	NzAbstractViewer* viewer = scene->GetViewer();
	const NzShader* lastShader = nullptr;
	const ShaderUniforms* shaderUniforms = nullptr;
	unsigned int lightCount = 0;

	for (unsigned int index : m_renderQueue.transparentModels)
	{
		const NzForwardRenderQueue::TransparentModelData& modelData = m_renderQueue.transparentModelData[index];

		// Matériau
		const NzMaterial* material = modelData.material;

		// On commence par appliquer du matériau (et récupérer le shader ainsi activé)
		const NzShader* shader = material->Apply();

		// 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());

			// On envoie les lumières directionnelles s'il y a (Les mêmes pour tous)
			if (shaderUniforms->hasLightUniforms)
			{
				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, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
			}

			lastShader = shader;
		}

		// Mesh
		const NzMatrix4f& matrix = modelData.transformMatrix;
		const NzMeshData& meshData = modelData.meshData;

		const NzIndexBuffer* indexBuffer = meshData.indexBuffer;
		const NzVertexBuffer* vertexBuffer = meshData.vertexBuffer;

		// Gestion du draw call avant la boucle de rendu
		NzRenderer::DrawCall drawFunc;
		unsigned int indexCount;

		if (indexBuffer)
		{
			drawFunc = NzRenderer::DrawIndexedPrimitives;
			indexCount = indexBuffer->GetIndexCount();
		}
		else
		{
			drawFunc = NzRenderer::DrawPrimitives;
			indexCount = vertexBuffer->GetVertexCount();
		}

		NzRenderer::SetIndexBuffer(indexBuffer);
		NzRenderer::SetVertexBuffer(vertexBuffer);

		if (shaderUniforms->hasLightUniforms)
		{
			// Calcul des lumières les plus proches
			if (lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS && !m_lights.IsEmpty())
			{
				NzVector3f position = matrix.GetTranslation() + modelData.squaredBoundingSphere.GetPosition();
				float radius = modelData.squaredBoundingSphere.radius;
				unsigned int closestLightCount = m_lights.ComputeClosestLights(position, radius, NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS);

				unsigned int count = std::min(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS - lightCount, closestLightCount);
				for (unsigned int i = 0; i < count; ++i)
					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, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
		}

		NzRenderer::SetMatrix(nzMatrixType_World, matrix);
		drawFunc(meshData.primitiveMode, 0, indexCount);
	}
}
void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const
{
	NzAbstractViewer* viewer = scene->GetViewer();
	const NzShader* lastShader = nullptr;
	const ShaderUniforms* shaderUniforms = nullptr;

	for (auto& matIt : m_renderQueue.opaqueModels)
	{
		auto& matEntry = matIt.second;

		if (matEntry.enabled)
		{
			NzForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap;

			if (!meshInstances.empty())
			{
				const NzMaterial* material = matIt.first;

				// Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active
				// Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches
				// (Le deferred shading n'a pas ce problème)
				bool instancing = m_instancingEnabled && (!material->IsLightingEnabled() || m_lights.IsEmpty()) && matEntry.instancingEnabled;

				// On commence par appliquer du matériau (et récupérer le shader ainsi activé)
				const NzShader* shader = material->Apply((instancing) ? nzShaderFlags_Instancing : 0);

				// 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;
				}

				// Meshes
				for (auto& meshIt : meshInstances)
				{
					const NzMeshData& meshData = meshIt.first;
					auto& meshEntry = meshIt.second;

					const NzSpheref& squaredBoundingSphere = meshEntry.squaredBoundingSphere;
					std::vector<NzMatrix4f>& instances = meshEntry.instances;

					if (!instances.empty())
					{
						const NzIndexBuffer* indexBuffer = meshData.indexBuffer;
						const NzVertexBuffer* vertexBuffer = meshData.vertexBuffer;

						// Gestion du draw call avant la boucle de rendu
						NzRenderer::DrawCall drawFunc;
						NzRenderer::DrawCallInstanced instancedDrawFunc;
						unsigned int indexCount;

						if (indexBuffer)
						{
							drawFunc = NzRenderer::DrawIndexedPrimitives;
							instancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced;
							indexCount = indexBuffer->GetIndexCount();
						}
						else
						{
							drawFunc = NzRenderer::DrawPrimitives;
							instancedDrawFunc = NzRenderer::DrawPrimitivesInstanced;
							indexCount = vertexBuffer->GetVertexCount();
						}

						NzRenderer::SetIndexBuffer(indexBuffer);
						NzRenderer::SetVertexBuffer(vertexBuffer);

						if (instancing)
						{
							// On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing)
							NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer();
							instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4));

							// Avec l'instancing, impossible de sélectionner les lumières pour chaque objet
							// Du coup, il n'est activé que pour les lumières directionnelles
							unsigned int lightCount = m_directionalLights.GetLightCount();
							unsigned int lightIndex = 0;
							nzRendererComparison oldDepthFunc = NzRenderer::GetDepthFunc();

							unsigned int passCount = (lightCount == 0) ? 1 : (lightCount-1)/NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1;
							for (unsigned int pass = 0; pass < passCount; ++pass)
							{
								if (shaderUniforms->hasLightUniforms)
								{
									unsigned int renderedLightCount = std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U));
									lightCount -= renderedLightCount;

									if (pass == 1)
									{
										// Pour additionner le résultat des calculs de lumière
										// Aucune chance d'interférer avec les paramètres du matériau car nous ne rendons que les objets opaques
										// (Autrement dit, sans blending)
										// Quant à la fonction de profondeur, elle ne doit être appliquée que la première fois
										NzRenderer::Enable(nzRendererParameter_Blend, true);
										NzRenderer::SetBlendFunc(nzBlendFunc_One, nzBlendFunc_One);
										NzRenderer::SetDepthFunc(nzRendererComparison_Equal);
									}

									for (unsigned int i = 0; i < renderedLightCount; ++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, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
								}

								const NzMatrix4f* instanceMatrices = &instances[0];
								unsigned int instanceCount = instances.size();
								unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Le nombre maximum d'instances en une fois

								while (instanceCount > 0)
								{
									// On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing)
									unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount);
									instanceCount -= renderedInstanceCount;

									// On remplit l'instancing buffer avec nos matrices world
									instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true);
									instanceMatrices += renderedInstanceCount;

									// Et on affiche
									instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount);
								}
							}

							// On n'oublie pas de désactiver le blending pour ne pas interférer sur le reste du rendu
							NzRenderer::Enable(nzRendererParameter_Blend, false);
							NzRenderer::SetDepthFunc(oldDepthFunc);
						}
						else
						{
							if (shaderUniforms->hasLightUniforms)
							{
								for (const NzMatrix4f& matrix : instances)
								{
									unsigned int directionalLightCount = m_directionalLights.GetLightCount();
									unsigned int otherLightCount = m_lights.ComputeClosestLights(matrix.GetTranslation() + squaredBoundingSphere.GetPosition(), squaredBoundingSphere.radius, m_maxLightPassPerObject*NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS - directionalLightCount);
									unsigned int lightCount = directionalLightCount + otherLightCount;

									NzRenderer::SetMatrix(nzMatrixType_World, matrix);
									unsigned int directionalLightIndex = 0;
									unsigned int otherLightIndex = 0;
									nzRendererComparison oldDepthFunc = NzRenderer::GetDepthFunc(); // Dans le cas où nous aurions à le changer

									unsigned int passCount = (lightCount == 0) ? 1 : (lightCount-1)/NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1;
									for (unsigned int pass = 0; pass < passCount; ++pass)
									{
										unsigned int renderedLightCount = std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U));
										lightCount -= renderedLightCount;

										if (pass == 1)
										{
											// Pour additionner le résultat des calculs de lumière
											// Aucune chance d'interférer avec les paramètres du matériau car nous ne rendons que les objets opaques
											// (Autrement dit, sans blending)
											// Quant à la fonction de profondeur, elle ne doit être appliquée que la première fois
											NzRenderer::Enable(nzRendererParameter_Blend, true);
											NzRenderer::SetBlendFunc(nzBlendFunc_One, nzBlendFunc_One);
											NzRenderer::SetDepthFunc(nzRendererComparison_Equal);
										}

										// On active les lumières de cette passe-ci
										for (unsigned int i = 0; i < renderedLightCount; ++i)
										{
											if (directionalLightIndex >= directionalLightCount)
												m_lights.GetResult(otherLightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);
											else
												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, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i);

										// Et on passe à l'affichage
										drawFunc(meshData.primitiveMode, 0, indexCount);
									}

									NzRenderer::Enable(nzRendererParameter_Blend, false);
									NzRenderer::SetDepthFunc(oldDepthFunc);
								}
							}
							else
							{
								// Sans instancing, on doit effectuer un draw call pour chaque instance
								// Cela reste néanmoins plus rapide que l'instancing en dessous d'un certain nombre d'instances
								// À cause du temps de modification du buffer d'instancing
								for (const NzMatrix4f& matrix : instances)
								{
									NzRenderer::SetMatrix(nzMatrixType_World, matrix);
									drawFunc(meshData.primitiveMode, 0, indexCount);
								}
							}
						}
						instances.clear();
					}
				}
			}

			// Et on remet à zéro les données
			matEntry.enabled = false;
			matEntry.instancingEnabled = false;
		}
	}
}
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();
			}
		}
	}
}
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;
		}
	}
}